
org.jsoftware.impl.DbManager Maven / Gradle / Ivy
package org.jsoftware.impl;
import org.jsoftware.config.AbstractPatch;
import org.jsoftware.config.ConfigurationEntry;
import org.jsoftware.config.Patch;
import org.jsoftware.config.RollbackPatch;
import org.jsoftware.config.dialect.Dialect;
import org.jsoftware.config.dialect.PatchExecutionResult;
import org.jsoftware.impl.extension.Extension;
import org.jsoftware.impl.statements.DisallowedSqlPatchStatement;
import org.jsoftware.log.Log;
import org.jsoftware.log.LogFactory;
import java.io.IOException;
import java.sql.*;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class DbManager {
private ConfigurationEntry ce;
private Dialect dialect;
private Connection c;
private final Log log = LogFactory.getInstance();
private List extensions;
public DbManager(ConfigurationEntry ce) throws SQLException {
this.ce = ce;
this.extensions = new LinkedList(ce.getExtensions());
this.dialect = ce.getDialect();
try {
Class.forName(ce.getDriverClass()).newInstance();
} catch(Exception ex) {
throw new SQLException("Could not load driver class - " + ce.getDriverClass());
}
}
public void init(DbManagerCredentialsCallback dbManagerPasswordCallback) throws SQLException {
Connection con;
int tryNo = 0;
String password = ce.getPassword();
do {
try {
con = DriverManager.getConnection(ce.getJdbcUri(), ce.getUser(), password);
} catch (SQLException e) {
password = dbManagerPasswordCallback.getPassword(e, tryNo, ce);
tryNo++;
continue;
}
break;
} while (true);
if (con != null) {
dialect.checkAndCreateStruct(con);
con.setAutoCommit(false); // just for sure
c = con;
}
}
public Connection getConnection() {
if (c == null) {
throw new IllegalStateException("Invoke init method first.");
}
return c;
}
public void updateStateObject(Patch p) throws SQLException {
PreparedStatement ps = c.prepareStatement("SELECT patch_db_date FROM "+ dialect.getDbPatchTableName() +" WHERE patch_name=?");
ps.setString(1, p.getName());
ResultSet rs = ps.executeQuery();
try {
if (rs.next()) {
Date d = rs.getDate(1);
if (d != null) {
p.setDbState(AbstractPatch.DbState.COMMITTED);
p.setDbDate(d);
} else {
p.setDbState(AbstractPatch.DbState.IN_PROGRESS);
}
} else {
p.setDbState(AbstractPatch.DbState.NOT_AVAILABLE);
}
} finally {
rs.close();
ps.close();
}
}
public void apply(final Patch p) throws SQLException {
PatchStatementHolder h = new PatchStatementHolder();
try {
log.info("Patch " + p.getName());
dialect.savePatchInfoPrepare(c, p);
invokeExtensions("beforePatch", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.beforePatch(c, p);
}
});
execute(p, h);
dialect.savePatchInfoFinal(c, p);
invokeExtensions("afterPatchComplete", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterPatch(c, p, null);
}
});
c.commit();
log.debug("Patch " + p.getName() + " committed.");
} catch (Exception e) {
if (h.object != null) {
log.warn("Query execution problem \"" + h.object + "\" - " + e);
}
log.warn("Patch " + p.getName() + " execution error!" + e);
c.rollback();
final SQLException ex = new SQLException(e.getMessage(), "");
ex.initCause(e);
invokeExtensions("afterPatchError", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterPatch(c, p, ex);
}
});
throw ex;
} finally {
updateStateObject(p);
}
}
/**
* @param p rollback patch
* @throws SQLException
*/
public void rollback(final RollbackPatch p) throws SQLException {
PatchStatementHolder h = new PatchStatementHolder();
try {
log.info("Patch " + p.getName());
invokeExtensions("beforeRollbackPatch", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.beforeRollbackPatch(c, p);
}
});
execute(p, h);
invokeExtensions("afterRollbackPatchComplete", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterRollbackPatch(c, p, null);
}
});
dialect.removePatchInfo(c, p);
c.commit();
log.debug("Patch " + p.getName() + " committed.");
} catch (Exception e) {
if (h.object != null) {
log.warn("Query execution problem \"" + h.object + "\" - " + e);
}
log.warn("Patch " + p.getName() + " execution error!" + e);
c.rollback();
final SQLException ex = new SQLException(e.getMessage(), "");
ex.initCause(e);
invokeExtensions("afterRollbackPatchError", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterRollbackPatch(c, p, ex);
}
});
throw ex;
}
}
private void execute(final AbstractPatch patch, final PatchStatementHolder h) throws IOException, SQLException {
for(final PatchStatement ps : ce.getPatchParser().parse(patch, ce).getStatements()) {
if (ps.isDisplayable()) {
log.debug(ps.toString());
}
if (ps.isExecutable()) {
h.object = ps;
invokeExtensions("beforePatchStatement", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.beforePatchStatement(c, patch, ps);
}
});
final PatchExecutionResult result = dialect.executeStatement(c, ps);
invokeExtensions("afterPatchStatement", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterPatchStatement(c, patch, result);
}
});
if (result.getCause() != null) {
throw result.getCause();
}
}
if (ps instanceof DisallowedSqlPatchStatement) {
log.warn("Skip disallowed statement " + ps.getCode());
}
} // for
h.object = null;
}
public void updateStateObjectAll(Collection patches) throws SQLException {
for(Patch p : patches) {
updateStateObject(p);
}
}
public void startExecution() throws SQLException {
dialect.lock(c, 3000);
invokeExtensions("beforePatching", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.beforePatching(c);
}
});
}
public void endExecution() throws SQLException {
try {
c.rollback();
} finally {
invokeExtensions("afterPatching", new ExtensionMethodInvokeCallback() {
public void invokeOn(Extension extension) throws Exception {
extension.afterPatching(c);
}
});
dialect.releaseLock(c);
}
}
public void dispose() {
CloseUtil.close(c);
}
private void invokeExtensions(String method, ExtensionMethodInvokeCallback cb) {
for(Extension extension : extensions) {
log.debug("Invoke method (" + method + ") on " + extension.getClass());
try {
cb.invokeOn(extension);
} catch (Exception e) {
log.warn("Invoke method (" + method + ") on " + extension.getClass() + " throws " + e, e);
}
}
}
public void addExtension(Extension extension) {
extensions.add(extension);
}
public String getTableName() {
return dialect.getDbPatchTableName();
}
public Date getNow() throws SQLException {
Timestamp ts = dialect.getNow(c);
return new Date(ts.getTime());
}
}
interface ExtensionMethodInvokeCallback {
void invokeOn(Extension extension) throws Exception;
}
class PatchStatementHolder {
public PatchStatement object;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy