All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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