001package fr.ifremer.adagio.core.action;
002
003/*
004 * #%L
005 * SIH-Adagio :: Shared
006 * $Id:$
007 * $HeadURL:$
008 * %%
009 * Copyright (C) 2012 - 2014 Ifremer
010 * %%
011 * This program is free software: you can redistribute it and/or modify
012 * it under the terms of the GNU Affero General Public License as published by
013 * the Free Software Foundation, either version 3 of the License, or
014 * (at your option) any later version.
015 * 
016 * This program is distributed in the hope that it will be useful,
017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
019 * GNU General Public License for more details.
020 * 
021 * You should have received a copy of the GNU Affero General Public License
022 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
023 * #L%
024 */
025
026import java.io.File;
027import java.io.IOException;
028import java.sql.Connection;
029import java.sql.PreparedStatement;
030import java.sql.SQLException;
031import java.util.List;
032import java.util.Properties;
033import java.util.Set;
034
035import org.apache.commons.io.FileUtils;
036import org.apache.commons.lang3.ArrayUtils;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039import org.nuiton.i18n.I18n;
040
041import com.google.common.base.Charsets;
042import com.google.common.base.Preconditions;
043import com.google.common.base.Predicate;
044import com.google.common.collect.Lists;
045import com.google.common.collect.Sets;
046import com.google.common.io.Files;
047
048import fr.ifremer.adagio.core.config.AdagioConfiguration;
049import fr.ifremer.adagio.synchro.SynchroTechnicalException;
050import fr.ifremer.adagio.synchro.dao.DaoUtils;
051
052public class AllegroCoreNewEmptyDbAction {
053    /* Logger */
054    private static final Log log = LogFactory.getLog(AllegroCoreNewEmptyDbAction.class);
055
056    private static String HSQLDB_SRC_DATABASE_CREATE_SCRIPT = "changelog/allegro.script";
057
058    public void run() {
059        AdagioConfiguration config = AdagioConfiguration.getInstance();
060
061        // Log target connection
062        if (log.isInfoEnabled()) {
063            log.info("Creating a new empty database...");
064        }
065
066        // Check output directory validity
067        File outputDirectory = config.getLiquibaseOutputFile();
068        if (outputDirectory == null) {
069            log.error("No output directory. Please use the option: --output <OUTPUT_DIRECTORY>");
070            System.exit(1);
071        }
072        if (log.isInfoEnabled()) {
073            log.info(" Output directory: " + outputDirectory);
074        }
075        if (outputDirectory.exists() && !outputDirectory.isDirectory()) {
076            log.error("Not a directory: " + outputDirectory);
077            System.exit(1);
078        }
079
080        // Make sure the directory could be created
081        try {
082            FileUtils.forceMkdir(outputDirectory);
083        } catch (IOException e) {
084            throw new SynchroTechnicalException(
085                    I18n.t("adagio.persistence.newEmptyDatabase.mkdir.error", outputDirectory),
086                    e);
087        }
088
089        if (ArrayUtils.isNotEmpty(outputDirectory.listFiles())) {
090            log.error("The output directory should be empty: " + outputDirectory);
091            System.exit(1);
092        }
093
094        // Set the database directory into the configuration
095        config.setDbDirectory(outputDirectory);
096
097        // Get connections properties :
098        Properties targetConnectionProperties = config.getConnectionProperties();
099
100        // Check connections
101        if (!checkConnection(config, targetConnectionProperties)) {
102            return;
103        }
104
105        try {
106            // Create the database
107            createEmptyDb(targetConnectionProperties);
108        } catch (SQLException e) {
109            throw new SynchroTechnicalException(
110                    I18n.t("adagio.persistence.newEmptyDatabase.create.error"),
111                    e);
112        } catch (IOException e) {
113            throw new SynchroTechnicalException(
114                    I18n.t("adagio.persistence.newEmptyDatabase.create.error"),
115                    e);
116        }
117
118        try {
119            // Shutdown database at end
120            DaoUtils.shutdownDatabase(targetConnectionProperties);
121        } catch (SQLException e) {
122            throw new SynchroTechnicalException(
123                    I18n.t("adagio.persistence.newEmptyDatabase.shutdown.error"),
124                    e);
125        }
126    }
127
128    protected boolean checkConnection(
129            AdagioConfiguration config,
130            Properties targetConnectionProperties) {
131
132        // Log target connection
133        if (log.isInfoEnabled()) {
134            log.info("Connecting to target database...");
135            log.info(String.format(" Database directory: %s", config.getDbDirectory()));
136            log.info(String.format(" JDBC Driver: %s", config.getJdbcDriver()));
137            log.info(String.format(" JDBC URL: %s", config.getJdbcURL()));
138            log.info(String.format(" JDBC Username: %s", config.getJdbcUsername()));
139        }
140
141        // Check target connection
142        boolean isValidConnection = DaoUtils.isValidConnectionProperties(targetConnectionProperties);
143        if (!isValidConnection) {
144            log.error("Connection error: could not connect to target database.");
145            return false;
146        }
147
148        return true;
149    }
150
151    public void createEmptyDb(Properties targetConnectionProperties) throws SQLException, IOException {
152
153        File scriptFile = new File(HSQLDB_SRC_DATABASE_CREATE_SCRIPT);
154        Preconditions.checkState(scriptFile.exists(), "Could not find db script at " + scriptFile);
155
156        if (log.isInfoEnabled()) {
157            log.info("Will use create script: " + scriptFile);
158        }
159        Connection connection = DaoUtils.createConnection(targetConnectionProperties);
160        try {
161            List<String> importScriptSql = getImportScriptSql(scriptFile);
162            for (String sql : importScriptSql) {
163                PreparedStatement statement = null;
164                try {
165                    statement = connection.prepareStatement(sql);
166                    statement.execute();
167                } catch (SQLException sqle) {
168                    log.warn("SQL command failed : " + sql, sqle);
169                    throw sqle;
170                } finally {
171                    DaoUtils.closeSilently(statement);
172                }
173
174            }
175            connection.commit();
176        } finally {
177            DaoUtils.closeSilently(connection);
178        }
179    }
180
181    protected List<String> getImportScriptSql(File scriptFile) throws IOException {
182        List<String> lines = Files.readLines(scriptFile, Charsets.UTF_8);
183
184        List<String> result = Lists.newArrayListWithCapacity(lines.size());
185
186        Predicate<String> predicate = new Predicate<String>() {
187
188            Set<String> forbiddenStarts = Sets.newHashSet(
189                    "SET ",
190                    "CREATE USER ",
191                    "CREATE SCHEMA ",
192                    "GRANT DBA TO ");
193
194            @Override
195            public boolean apply(String input) {
196                boolean accept = true;
197                for (String forbiddenStart : forbiddenStarts) {
198                    if (input.startsWith(forbiddenStart)) {
199                        accept = false;
200                        break;
201                    }
202                }
203                return accept;
204            }
205        };
206        for (String line : lines) {
207            if (predicate.apply(line.trim().toUpperCase())) {
208                if (line.contains("\\u000a")) {
209                    line = line.replaceAll("\\\\u000a", "\n");
210                }
211                result.add(line);
212            }
213        }
214        return result;
215    }
216}