package dbmigrate.executor;
import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import dbmigrate.exceptions.HistoryException;
import dbmigrate.exceptions.ValidationException;
import dbmigrate.executor.oracle.OracleAddColumnExecutor;
import dbmigrate.executor.oracle.OracleCreateTableExecutor;
import dbmigrate.executor.oracle.OracleDropColumnExecutor;
import dbmigrate.executor.oracle.OracleDropTableExecutor;
import dbmigrate.logging.HistoryStorage;
import dbmigrate.logging.ILogger;
import dbmigrate.logging.Level;
import dbmigrate.model.operation.AddColumnOperationDescriptor;
import dbmigrate.model.operation.ChangeColumnOperationDescriptor;
import dbmigrate.model.operation.CreateTableOperationDescriptor;
import dbmigrate.model.operation.DropColumnOperationDescriptor;
import dbmigrate.model.operation.DropTableOperationDescriptor;
import dbmigrate.model.operation.IOperationDescriptor;
import dbmigrate.model.operation.MergeColumnOperationDescriptor;
import dbmigrate.model.operation.MigrationConfiguration;
import dbmigrate.model.operation.ModifyColumnOperationDescriptor;
import dbmigrate.model.operation.RenameColumnOperationDescriptor;
import dbmigrate.model.operation.SplitColumnOperationDescriptor;
public class ExecutorEngine {
private Connection connection;
private MigrationConfiguration migrationConfiguration;
private boolean autoCommitEnable = true;
private ILogger logger;
private boolean forwards = true;
private HistoryStorage storage;
private Map<Class<? extends IOperationDescriptor>, Class<? extends IExecutor>> executors;
public ExecutorEngine(Connection connection,
MigrationConfiguration migrationConfiguration, boolean atomicity) {
this.connection = connection;
this.migrationConfiguration = migrationConfiguration;
if (atomicity) {
try {
connection.setAutoCommit(false);
this.autoCommitEnable = false;
} catch (SQLException e) {
e.printStackTrace();
}
}
this.executors = new LinkedHashMap<Class<? extends IOperationDescriptor>, Class<? extends IExecutor>>();
}
public void setHistoryStorage(HistoryStorage storage) {
this.storage = storage;
}
public void setForwards(boolean forwards) {
this.forwards = forwards;
}
public boolean getForwards() {
return this.forwards;
}
public void registerExecutor(
Class<? extends IOperationDescriptor> descriptorClass,
Class<? extends IExecutor> executorClass) {
this.executors.put(descriptorClass, executorClass);
}
public void registerPostgresExecutors() {
this.registerExecutor(AddColumnOperationDescriptor.class,
AddColumnExecutor.class);
this.registerExecutor(DropTableOperationDescriptor.class,
DropTableExecutor.class);
this.registerExecutor(DropColumnOperationDescriptor.class,
DropColumnExecutor.class);
this.registerExecutor(CreateTableOperationDescriptor.class,
CreateTableExecutor.class);
this.registerExecutor(RenameColumnOperationDescriptor.class,
RenameColumnExecutor.class);
this.registerExecutor(ModifyColumnOperationDescriptor.class,
ModifyColumnExecutor.class);
this.registerExecutor(ChangeColumnOperationDescriptor.class,
ChangeColumnExecutor.class);
this.registerExecutor(SplitColumnOperationDescriptor.class,
SplitColumnExecutor.class);
this.registerExecutor(MergeColumnOperationDescriptor.class,
MergeColumnExecutor.class);
}
public void registerOracleExecutors() {
this.registerExecutor(AddColumnOperationDescriptor.class,
OracleAddColumnExecutor.class);
this.registerExecutor(DropColumnOperationDescriptor.class,
OracleDropColumnExecutor.class);
this.registerExecutor(DropTableOperationDescriptor.class,
OracleDropTableExecutor.class);
this.registerExecutor(CreateTableOperationDescriptor.class,
OracleCreateTableExecutor.class);
}
public boolean executeMigration() throws HistoryException, SQLException {
StringBuilder sb = new StringBuilder();
boolean areErrors = false;
boolean isSuccess = true;
Map<IOperationDescriptor, IExecutor> localExecutors = new LinkedHashMap<IOperationDescriptor, IExecutor>();
for (IOperationDescriptor operation : this.migrationConfiguration
.getOperations(this.forwards)) {
try {
// You can register new executors in Application.java now.
Class<? extends IExecutor> cls = this.executors.get(operation
.getClass());
Constructor<? extends IExecutor> constructor = cls
.getConstructor(Connection.class);
IExecutor executor = constructor.newInstance(this.connection);
executor.validate(operation);
localExecutors.put(operation, executor);
} catch (ValidationException e) {
areErrors = true;
isSuccess = false;
this.logger.log(e.getMessage(), Level.Error);
} catch (Exception e) {
areErrors = true;
isSuccess = false;
this.logger.log(
"Error in the executor definition: " + e.getMessage(),
Level.Error);
}
}
if (!areErrors) {
this.logger.log("Executor validation completed.", Level.Info);
try {
for (Map.Entry<IOperationDescriptor, IExecutor> entry : localExecutors
.entrySet()) {
String className = this.classNamePrettifier(entry.getKey()
.getClass().toString());
this.logger.log("Executing " + className, Level.Info);
entry.getValue().execute(entry.getKey());
sb.append(className).append("\n");
}
if (!this.autoCommitEnable) {
try {
this.logger.log("Committing changes...", Level.Info);
this.connection.commit();
this.logger.log("Transaction committed.", Level.Info);
this.autoCommitEnable = true;
} catch (SQLException e) {
this.logger.log(e.getSQLState(), Level.Error);
this.logger.log(e.getMessage(), Level.Error);
isSuccess = false;
}
}
} catch (SQLException e) {
isSuccess = false;
this.logger.log(e.getMessage(), Level.Error);
if (!this.autoCommitEnable) {
try {
this.logger.log("Rolling back the transaction...",
Level.Info);
this.connection.rollback();
this.logger.log("Transaction rolled back.", Level.Info);
this.autoCommitEnable = true;
} catch (SQLException ex) {
this.logger.log(
"Cannot rollback the transaction, I'm sorry: "
+ ex.getMessage(), Level.Error);
isSuccess = false;
}
}
}
}
if (null != this.storage) {
this.storage.store("0.0.0.0",
this.migrationConfiguration.getMigrationId(),
(new Date()).toString(), (this.forwards ? 0 : 1),
sb.toString(), isSuccess);
}
return isSuccess;
}
private String classNamePrettifier(String className) {
String[] cn = className.split("\\.");
String newName = cn[cn.length - 1];
String prettyName = newName.replace("Descriptor", "");
return prettyName;
}
public void setLogger(ILogger logger) {
this.logger = logger;
}
}