/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.rrm.ehour.persistence.dbvalidator; import net.rrm.ehour.config.ConfigurationItem; import org.apache.commons.beanutils.DynaBean; import org.apache.ddlutils.DdlUtilsException; import org.apache.ddlutils.Platform; import org.apache.ddlutils.PlatformFactory; import org.apache.ddlutils.io.DataReader; import org.apache.ddlutils.io.DatabaseDataIO; import org.apache.ddlutils.io.DatabaseIO; import org.apache.ddlutils.model.Database; import org.apache.derby.jdbc.EmbeddedDataSource; import org.apache.log4j.Logger; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStreamReader; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; /** * Derby database accessor methods */ public class DerbyDbValidator { private static final String DDL_FILE = "derby/ddl/ddl-ehour-%s.xml"; private static final String DML_FILE = "derby/ddl/dml-ehour-%s.xml"; private static final String DML_DIFF_FILE = "derby/ddl/dml-ehour-%s-diff.xml"; public enum DdlType {NONE, CREATE_TABLE, ALTER_TABLE} private static final Logger LOGGER = Logger.getLogger(DerbyDbValidator.class); private EmbeddedDataSource dataSource; private String requiredDbVersion; public DerbyDbValidator(String requiredDbVersion, DataSource dataSource) { this.requiredDbVersion = requiredDbVersion; this.dataSource = (EmbeddedDataSource) dataSource; } public DdlType checkDatabaseState() { boolean databaseInState; String currentVersion; DdlType ddlType; LOGGER.info("Verifying datamodel version. Minimum version: " + requiredDbVersion); Connection connection = null; try { dataSource.setCreateDatabase("create"); connection = dataSource.getConnection(); connection.setAutoCommit(false); currentVersion = getCurrentVersion(connection); databaseInState = (currentVersion != null) && currentVersion.equalsIgnoreCase(requiredDbVersion); if (databaseInState) { ddlType = DdlType.NONE; LOGGER.info("Datamodel is the required version."); } else { LOGGER.info("Datamodel of version " + currentVersion + " found. Upgrading to " + requiredDbVersion); ddlType = DdlType.ALTER_TABLE; } } catch (SQLException e) { ddlType = DdlType.CREATE_TABLE; LOGGER.info("Could not determine datamodel's version, recreating.."); } finally { dataSource.setCreateDatabase(""); try { if (connection != null) { connection.close(); } } catch (SQLException e) { // LOGGER.error("Failed to close connection", e); } } if (ddlType != DdlType.NONE) { try { createOrAlterDatamodel(dataSource, ddlType); } catch (Exception e) { LOGGER.error("Failed to create or upgrade datamodel", e); } } return ddlType; } /** * Create datamodel and fill with initial data */ private void createOrAlterDatamodel(DataSource dataSource, DdlType ddlType) throws DdlUtilsException, IOException { Platform platform = PlatformFactory.createNewPlatformInstance(dataSource); Resource resource = new ClassPathResource(getDdlFilename()); DatabaseIO reader = new DatabaseIO(); reader.setValidateXml(false); reader.setUseInternalDtd(true); Database ddlModel = reader.read(new InputStreamReader(resource.getInputStream(), "UTF-8")); if (ddlType == DdlType.CREATE_TABLE) { platform.createTables(ddlModel, false, false); insertInitialData(platform, ddlModel); } else { platform.alterTables(ddlModel, false); insertDiffData(platform, ddlModel); updateVersion(platform, ddlModel); } } /** * Insert data */ private void insertInitialData(Platform platform, Database model) throws DdlUtilsException, IOException { insertData(platform, model, getDmlFilename()); LOGGER.info("Data inserted"); } /** * Insert diff data (for existing db's) */ private void insertDiffData(Platform platform, Database model) throws DdlUtilsException, IOException { insertData(platform, model, getDmlDiffFilename()); LOGGER.info("Data updated"); } private void insertData(Platform platform, Database model, String filename) throws IOException { Resource resource = new ClassPathResource(filename); if (!resource.exists()) { return; } DatabaseDataIO dataIO = new DatabaseDataIO(); DataReader dataReader = dataIO.getConfiguredDataReader(platform, model); dataReader.getSink().start(); dataIO.writeDataToDatabase(dataReader, new InputStreamReader(resource.getInputStream(), "UTF-8")); } /** * Get current version of database state */ private String getCurrentVersion(Connection connection) throws SQLException { String version = null; try (PreparedStatement statement = connection.prepareStatement("SELECT config_value FROM CONFIGURATION WHERE config_key = ?")) { statement.setString(1, ConfigurationItem.VERSION.getDbField()); try (ResultSet results = statement.executeQuery()) { if (results.next()) { version = results.getString("config_value"); } } } return version; } private void updateVersion(Platform platform, Database database) { DynaBean configuration = database.createDynaBeanFor("CONFIGURATION", false); configuration.set("config_key", "version"); platform.delete(database, configuration); configuration.set("config_value", requiredDbVersion); platform.insert(database, configuration); } private String getDdlFilename() { return String.format(DDL_FILE, requiredDbVersion); } private String getDmlFilename() { return String.format(DML_FILE, requiredDbVersion); } private String getDmlDiffFilename() { return String.format(DML_DIFF_FILE, requiredDbVersion); } }