001/*
002 *  Copyright 2020 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.migration.action.impl;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.sql.Connection;
021import java.sql.SQLException;
022import java.util.Map;
023import java.util.Map.Entry;
024
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.commons.io.IOUtils;
031import org.apache.excalibur.source.Source;
032import org.apache.excalibur.source.SourceResolver;
033
034import org.ametys.core.datasource.ConnectionHelper;
035import org.ametys.core.migration.MigrationException;
036import org.ametys.core.migration.action.Action;
037import org.ametys.core.migration.action.data.ActionData;
038import org.ametys.core.migration.action.data.impl.SqlUpgradeActionData;
039import org.ametys.core.migration.version.Version;
040import org.ametys.core.migration.version.impl.SqlVersion;
041import org.ametys.core.script.SQLScriptHelper;
042import org.ametys.runtime.plugin.component.AbstractLogEnabled;
043
044/**
045 * SQL action: A script will be executed
046 */
047public class SqlUpgradeAction extends AbstractLogEnabled implements Action, Serviceable
048{
049    /** Source resolver */
050    protected SourceResolver _sourceResolver;
051
052    @Override
053    public void service(ServiceManager manager) throws ServiceException
054    {
055        _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
056    }
057    
058    @Override
059    public void doAction(ActionData actionData) throws MigrationException
060    {
061        getLogger().debug("Start ugprade for: {}", actionData.toString());
062        if (!(actionData instanceof SqlUpgradeActionData))
063        {
064            throw new MigrationException("SQL Upgrade can only be created for an SQL upgrade, this is not the case for upgrade: " + actionData.toString());
065        }
066        
067        SqlUpgradeActionData sqlActionData = (SqlUpgradeActionData) actionData;
068        Version version = sqlActionData.getVersion();
069        
070        if (!(version instanceof SqlVersion))
071        {
072            throw new MigrationException("SQL Upgrade can only be created with a SQL Version, this is not the case for upgrade: " + sqlActionData.toString());
073        }
074        
075        SqlVersion sqlVersion = (SqlVersion) version;
076        _upgrade(sqlActionData, sqlVersion);
077        
078        getLogger().debug("End upgrade for: {}", actionData.toString());
079    }
080
081    /**
082     * Do the upgrade with the right objects
083     * @param sqlUpgradeData upgrade data
084     * @param sqlVersion version
085     * @throws MigrationException Something went wrong
086     */
087    protected void _upgrade(SqlUpgradeActionData sqlUpgradeData, SqlVersion sqlVersion) throws MigrationException
088    {
089        Connection connection = null;
090        Source source = null;
091        try
092        {
093            String file = sqlUpgradeData.getFile();
094            
095            String datasourceId = sqlVersion.getDatasourceId();
096            connection = ConnectionHelper.getConnection(datasourceId);
097
098            file = "plugin:" + sqlUpgradeData.getPlugin() + "://" + file.replaceAll("%DATABASE_TYPE%", sqlVersion.getDatabaseType());
099            source = _sourceResolver.resolveURI(file);
100            
101            try (InputStream is = source.getInputStream())
102            {
103                String script = IOUtils.toString(is, "UTF-8");
104                
105                Map<String, Object> additionalScriptValue = sqlVersion.getAdditionalValues();
106                for (Entry<String, Object> keyValue : additionalScriptValue.entrySet())
107                {
108                    Object value = keyValue.getValue();
109                    if (value instanceof String)
110                    {
111                        script = script.replaceAll("%" + keyValue.getKey() + "%", (String) value);
112                    }
113                }
114
115                SQLScriptHelper.runScript(connection, script);
116            }
117        }
118        catch (SQLException | IOException e)
119        {
120            throw new MigrationException("Impossible to run the upgrade: " + sqlUpgradeData.toString(), e);
121        }
122        finally
123        {
124            ConnectionHelper.cleanup(connection);
125            _sourceResolver.release(source);
126        }
127    }
128
129    @Override
130    public ActionData generateActionData(String versionNumber, Version version, String comment, String from, String type, String pluginName, Configuration configuration) throws MigrationException, ConfigurationException
131    {
132        return new SqlUpgradeActionData(versionNumber, version, comment, from, type, pluginName, configuration);
133    }
134
135}