001/*
002 *  Copyright 2016 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.core.script;
017
018import java.sql.Connection;
019import java.util.HashSet;
020import java.util.Set;
021
022import org.apache.avalon.framework.configuration.Configurable;
023import org.apache.avalon.framework.configuration.Configuration;
024import org.apache.avalon.framework.configuration.ConfigurationException;
025import org.apache.avalon.framework.logger.AbstractLogEnabled;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.avalon.framework.service.Serviceable;
029import org.apache.commons.lang3.StringUtils;
030import org.apache.excalibur.source.SourceResolver;
031
032import org.ametys.core.datasource.ConnectionHelper;
033import org.ametys.core.datasource.SQLDataSourceManager;
034import org.ametys.runtime.config.Config;
035import org.ametys.runtime.plugin.Init;
036import org.ametys.runtime.plugin.component.PluginAware;
037
038/**
039 * Creates necessary SQL tables (if not already existing) at initialization.
040 */
041public class SqlTablesInit extends AbstractLogEnabled implements Init, Serviceable, Configurable, PluginAware
042{
043    /** Plugin name */
044    protected String _pluginName;
045    
046    /** The data source identifer */
047    protected String _dataSourceId;
048    
049    /** The set of configured table init scripts */
050    protected Set<InitScript> _scripts;
051    
052    /** SQL data source manager */
053    protected SQLDataSourceManager _sqlDataSourceManager;
054    
055    /** Source resolver */
056    protected SourceResolver _sourceResolver;
057    
058    @Override
059    public void setPluginInfo(String pluginName, String featureName, String id)
060    {
061        _pluginName = pluginName;
062    }
063    
064    @Override
065    public void service(ServiceManager manager) throws ServiceException
066    {
067        _sqlDataSourceManager = (SQLDataSourceManager) manager.lookup(SQLDataSourceManager.ROLE);
068        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
069    }
070    
071    @Override
072    public void configure(Configuration configuration) throws ConfigurationException
073    {
074        Configuration dataSourceConf = configuration.getChild("datasource", false);
075        if (dataSourceConf == null)
076        {
077            throw new ConfigurationException("The 'datasource' configuration node must be defined.", dataSourceConf);
078        }
079        
080        String dataSourceConfParam = dataSourceConf.getValue();
081        String dataSourceConfType = dataSourceConf.getAttribute("type", "config");
082        
083        if (StringUtils.equals(dataSourceConfType, "config"))
084        {
085            _dataSourceId = Config.getInstance().getValueAsString(dataSourceConfParam);
086        }
087        else // expecting type="id"
088        {
089            _dataSourceId = dataSourceConfParam;
090        }
091        
092        _scripts = new HashSet<>();
093        Configuration[] scripts = configuration.getChildren("script");
094        
095        for (Configuration scriptConf : scripts)
096        {
097            String pluginName = scriptConf.getAttribute("plugin", _pluginName);
098            
099            String testTable = scriptConf.getAttribute("testTable");
100            if (StringUtils.isBlank(testTable))
101            {
102                throw new ConfigurationException("The test table attribute cannot be blank.");
103            }
104            
105            String fileName = scriptConf.getValue();
106            if (StringUtils.isBlank(fileName))
107            {
108                throw new ConfigurationException("The SQL file name cannot be blank.");
109            }
110            
111            _scripts.add(new InitScript(pluginName, fileName, testTable));
112        }
113    }
114    
115    @Override
116    public void init() throws Exception
117    {
118        try
119        {
120            // Test and create tables
121            Connection connection = null;
122            try
123            {
124                connection = ConnectionHelper.getConnection(_dataSourceId);
125                
126                String scriptFolder = ConnectionHelper.getDatabaseType(connection);
127                
128                for (InitScript initScript : _scripts)
129                {
130                    SQLScriptHelper.createTableIfNotExists(connection, initScript._testTable, "plugin:" + initScript._pluginName + "://scripts/" + scriptFolder + "/" + initScript._fileName, _sourceResolver);
131                }
132            }
133            finally
134            {
135                ConnectionHelper.cleanup(connection);
136            }
137        }
138        catch (Exception e)
139        {
140            String errorMsg = String.format("Error during SQL tables initialization for data source id: '%s'.", StringUtils.defaultString(_dataSourceId));
141            getLogger().error(errorMsg, e);
142        }
143    }
144    
145    private static class InitScript
146    {
147        final String _pluginName;
148        final String _fileName;
149        final String _testTable;
150        
151        public InitScript(String pluginNameArg, String fileNameArg, String testTableArg)
152        {
153            _pluginName = pluginNameArg;
154            _fileName = fileNameArg;
155            _testTable = testTableArg;
156        }
157    }
158}