Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2008, Unitils.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.unitils.dbmaintainer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.unitils.core.UnitilsException;
import org.unitils.core.dbsupport.SQLHandler;
import org.unitils.core.util.ConfigUtils;
import org.unitils.dbmaintainer.clean.DBCleaner;
import org.unitils.dbmaintainer.clean.DBClearer;
import org.unitils.dbmaintainer.script.ExecutedScript;
import org.unitils.dbmaintainer.script.Script;
import org.unitils.dbmaintainer.script.ScriptRunner;
import org.unitils.dbmaintainer.script.ScriptSource;
import org.unitils.dbmaintainer.structure.ConstraintsDisabler;
import org.unitils.dbmaintainer.structure.DataSetStructureGenerator;
import org.unitils.dbmaintainer.structure.SequenceUpdater;
import static org.unitils.dbmaintainer.util.DatabaseModuleConfigUtils.getConfiguredDatabaseTaskInstance;
import org.unitils.dbmaintainer.version.ExecutedScriptInfoSource;
import org.unitils.dbmaintainer.version.Version;
import org.unitils.util.PropertyUtils;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.Set;
/**
* A class for performing automatic maintenance of a database.
* This class must be configured with implementations of a {@link ExecutedScriptInfoSource},
* {@link ScriptSource}, a {@link ScriptRunner}, {@link DBClearer}, {@link DBCleaner},
* {@link ConstraintsDisabler}, {@link SequenceUpdater} and a {@link DataSetStructureGenerator}
* The {@link #updateDatabase()} method check what is the current version of the database, and
* see if existing scripts have been modified. If yes, the database is cleared and all available
* database scripts, are executed on the database. If no existing scripts have been modified, but
* new scripts were added, only the new scripts are executed. Before executing an update, data from
* the database is removed, to avoid problems when e.g. adding a not null column. If a database
* update causes an error, a {@link UnitilsException} is thrown. After a failing update, the
* database is always completely recreated from scratch. After updating the database, following
* steps are optionally executed on the database (depending on the configuration):
*
*
Foreign key and not null constraints are disabled.
*
Sequences and identity columns that have a value lower than a configured treshold, are
* updated to a value equal to or larger than this treshold
*
A DTD is generated that describes the database's table structure, to use in test data XML
* files
*
* To obtain a properly configured DBMaintainer, invoke the constructor
* {@link #DBMaintainer(Properties,SQLHandler)} with a TestDataSource providing
* access to the database and a Configuration object containing all necessary
* properties.
*
* @author Filip Neven
* @author Tim Ducheyne
*/
public class DBMaintainer {
/* The logger instance for this class */
private static Log logger = LogFactory.getLog(DBMaintainer.class);
/**
* Property indicating if deleting all data from all tables before updating is enabled
*/
public static final String PROPKEY_DB_CLEANER_ENABLED = "dbMaintainer.cleanDb.enabled";
/**
* Property indicating if updating the database from scratch is enabled
*/
public static final String PROPKEY_FROM_SCRATCH_ENABLED = "dbMaintainer.fromScratch.enabled";
/**
* Property indicating if database code should be cleared before installing a new version of
* the code or when updating the database from scratch
*/
public static final String PROPKEY_CLEAR_DB_CODE_ENABLED = "dbMaintainer.clearDbCode.enabled";
/**
* Property indicating if an retry of an update should only be performed when changes to script files were made
*/
public static final String PROPKEY_KEEP_RETRYING_AFTER_ERROR_ENABLED = "dbMaintainer.keepRetryingAfterError.enabled";
/**
* Property indicating if the database constraints should org disabled after updating the database
*/
public static final String PROPKEY_DISABLE_CONSTRAINTS_ENABLED = "dbMaintainer.disableConstraints.enabled";
/**
* Property indicating if the database constraints should org disabled after updating the database
*/
public static final String PROPKEY_UPDATE_SEQUENCES_ENABLED = "dbMaintainer.updateSequences.enabled";
/**
* Property that indicates if a data set DTD or XSD is to be generated or not
*/
public static final String PROPKEY_GENERATE_DATA_SET_STRUCTURE_ENABLED = "dbMaintainer.generateDataSetStructure.enabled";
/**
* Provider of the current version of the database, and means to increment it
*/
protected ExecutedScriptInfoSource versionSource;
/**
* Provider of scripts for updating the database to a higher version
*/
protected ScriptSource scriptSource;
/**
* Executer of the scripts
*/
protected ScriptRunner scriptRunner;
/**
* Clearer of the database (removed all tables, sequences, ...) before updating
*/
protected DBClearer dbClearer;
/**
* Cleaner of the database (deletes all data from all tables before updating
*/
protected DBCleaner dbCleaner;
/**
* Disabler of constraints
*/
protected ConstraintsDisabler constraintsDisabler;
/**
* Database sequence updater
*/
protected SequenceUpdater sequenceUpdater;
/**
* Database DTD generator
*/
protected DataSetStructureGenerator dataSetStructureGenerator;
/**
* Indicates whether updating the database from scratch is enabled. If true, the database is
* cleared before updating if an already executed script is modified
*/
protected boolean fromScratchEnabled;
/**
* Indicates if foreign key and not null constraints should removed after updating the database
* structure
*/
protected boolean disableConstraintsEnabled;
/**
* Indicates whether a from scratch update should be performed when the previous update failed,
* but none of the scripts were modified since that last update. If true a new update will be
* tried only when changes were made to the script files
*/
protected boolean keepRetryingAfterError;
/**
* Default constructor for testing.
*/
protected DBMaintainer() {
}
/**
* Create a new instance of DBMaintainer, The concrete implementations of all
* helper classes are derived from the given Configuration object.
*
* @param configuration the configuration, not null
* @param sqlHandler the data source, not null
*/
public DBMaintainer(Properties configuration, SQLHandler sqlHandler) {
try {
scriptRunner = getConfiguredDatabaseTaskInstance(ScriptRunner.class, configuration, sqlHandler);
versionSource = getConfiguredDatabaseTaskInstance(ExecutedScriptInfoSource.class, configuration, sqlHandler);
scriptSource = ConfigUtils.getConfiguredInstanceOf(ScriptSource.class, configuration);
boolean cleanDbEnabled = PropertyUtils.getBoolean(PROPKEY_DB_CLEANER_ENABLED, configuration);
if (cleanDbEnabled) {
dbCleaner = getConfiguredDatabaseTaskInstance(DBCleaner.class, configuration, sqlHandler);
}
fromScratchEnabled = PropertyUtils.getBoolean(PROPKEY_FROM_SCRATCH_ENABLED, configuration);
keepRetryingAfterError = PropertyUtils.getBoolean(PROPKEY_KEEP_RETRYING_AFTER_ERROR_ENABLED, configuration);
if (fromScratchEnabled) {
dbClearer = getConfiguredDatabaseTaskInstance(DBClearer.class, configuration, sqlHandler);
}
disableConstraintsEnabled = PropertyUtils.getBoolean(PROPKEY_DISABLE_CONSTRAINTS_ENABLED, configuration);
constraintsDisabler = getConfiguredDatabaseTaskInstance(ConstraintsDisabler.class, configuration, sqlHandler);
boolean updateSequences = PropertyUtils.getBoolean(PROPKEY_UPDATE_SEQUENCES_ENABLED, configuration);
if (updateSequences) {
sequenceUpdater = getConfiguredDatabaseTaskInstance(SequenceUpdater.class, configuration, sqlHandler);
}
boolean generateDtd = PropertyUtils.getBoolean(PROPKEY_GENERATE_DATA_SET_STRUCTURE_ENABLED, configuration);
if (generateDtd) {
dataSetStructureGenerator = getConfiguredDatabaseTaskInstance(DataSetStructureGenerator.class, configuration, sqlHandler);
}
} catch (UnitilsException e) {
logger.error("Error while initializing DbMaintainer", e);
throw e;
}
}
/**
* Checks if the new scripts are available to update the version of the database. If yes, these
* scripts are executed and the version number is increased. If an existing script has been
* modified, the database is cleared and completely rebuilt from scratch. If an error occurs
* with one of the scripts, a {@link UnitilsException} is thrown.
*/
public void updateDatabase() {
// Check if the executed scripts info source recommends a from-scratch update
boolean fromScratchUpdateRecommended = versionSource.isFromScratchUpdateRecommended();
Set alreadyExecutedScripts = versionSource.getExecutedScripts();
Version highestExecutedScriptVersion = getHighestExecutedScriptVersion(alreadyExecutedScripts);
// check whether an from scratch update should be performed
boolean shouldUpdateFromScratch = shouldUpdateDatabaseFromScratch(highestExecutedScriptVersion, alreadyExecutedScripts);
if (fromScratchEnabled && (fromScratchUpdateRecommended || shouldUpdateFromScratch)) {
// From scratch needed, clear the database and retrieve scripts
// constraints are removed before clearing the database, to be sure there will be no
// conflicts when dropping tables
constraintsDisabler.disableConstraints();
dbClearer.clearSchemas();
// reset the database version
versionSource.clearAllExecutedScripts();
// update database with all scripts
updateDatabase(scriptSource.getAllUpdateScripts());
return;
}
// perform an incremental update
updateDatabase(scriptSource.getNewScripts(highestExecutedScriptVersion, alreadyExecutedScripts));
}
protected Version getHighestExecutedScriptVersion(Set executedScripts) {
Version highest = new Version("0");
for (ExecutedScript executedScript : executedScripts) {
if (executedScript.getScript().isIncremental()) {
if (executedScript.getScript().getVersion().compareTo(highest) > 0) {
highest = executedScript.getScript().getVersion();
}
}
}
return highest;
}
/**
* Updates the database version to the current version of the update scripts, without changing
* anything else in the database. Can be used to initialize the database for future updates,
* knowning that the current state of the database is synchronized with the current state of the
* scripts.
*/
public void resetDatabaseState() {
versionSource.clearAllExecutedScripts();
List