/*****************************************************************************
* Copyright 2012 celum Slovakia s r.o.
*
* 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 com.celum.dbtool;
import com.celum.dbtool.installer.DbEventListener;
import com.celum.dbtool.installer.DbInstaller;
import com.celum.dbtool.resource.DbScriptsResource;
import com.celum.dbtool.resource.DirResource;
import com.celum.dbtool.resource.PackageResource;
import com.celum.dbtool.resource.VersionFilter;
import com.celum.dbtool.script.Version;
import com.celum.dbtool.script.VersionFactory;
import com.celum.dbtool.sql.*;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* This is the main facade for DB tool. You will use
* mostly this class.
*
* @author Zdenko Vrabel (zdenko.vrabel@celum.com)
*/
public class Db {
/** database connection */
private Connection dbConnection;
/** resource from where scripts comes */
private DbScriptsResource sqlScripts;
/** variables which will be replaced as placeholders in SQLs */
private Map<String, Object> variables;
/** listener reacting on events like start executing SQL etc. */
private DbEventListener eventListener;
/** version factory construct concrete implementation of {@link Version} */
private VersionFactory versionFactory;
/** Sql script which updates the version in DB */
private String versionUpdateSqlScript;
/** holds current DB version, it's filled by getCurrentVersion() method */
private Version currentDbVersion;
/**
* Builder helps with construction of Db.
*/
public class DbBuilder
{
private DbBuilder() { }
public DbBuilder withEventsListener(DbEventListener eventListener)
{
eventListener = eventListener;
return this;
}
public DbBuilder withVariable(String name, Object value)
{
variables.put(name, value);
return this;
}
public DbBuilder withVariables(Map<?,?> vars)
{
if (vars != null) {
for (Map.Entry<?,?> entry : vars.entrySet()) {
withVariable(entry.getKey().toString(), entry.getValue());
}
}
return this;
}
public DbBuilder withProperties(Properties properties)
{
for (Map.Entry<Object,Object> e : properties.entrySet()) {
withVariable(e.getKey().toString(), e.getValue().toString());
}
return this;
}
public DbBuilder withVersionFactory(VersionFactory vf)
{
if (vf != null) {
Db.this.versionFactory = vf;
}
return this;
}
public DbBuilder withVersionUpdate(String versionUpdateSql)
{
Db.this.versionUpdateSqlScript = versionUpdateSql;
return this;
}
public Db andDbScripts(DbScriptsResource sqlScripts)
{
if (sqlScripts == null) {
throw new IllegalArgumentException("sqlScripts resource is null");
}
Db.this.sqlScripts = sqlScripts;
return Db.this;
}
public Db andDbScriptsInDir(File dir)
{
return andDbScripts(new DirResource(dir));
}
public Db andDbScriptsInPackage(Package pckg)
{
return andDbScripts(new PackageResource(pckg));
}
public Db andDbScriptsInPackage(String packageName)
{
return andDbScripts(new PackageResource(packageName));
}
}
/**
* Constructor
*/
private Db(Connection con) {
this.dbConnection = con;
this.variables = new HashMap<String, Object>();
}
public static DbBuilder withJdbcConnection(Connection con)
{
return (new Db(con)).new DbBuilder();
}
/**
* Install all scripts that comes from script resource.
*
* @throws IOException
* @throws SQLException
*/
public void install() throws IOException, SQLException
{
executeScriptsFromResource(sqlScripts);
}
/**
* Method update the database to latest version.
*/
public void patch(String versionSql)
{
//decorate resource with version filter
getCurrentDbVersion(versionSql);
DbScriptsResource filteredScripts =
VersionFilter
.filter(sqlScripts)
.largerThan(currentDbVersion);
executeScriptsFromResource(filteredScripts);
}
/**
* Method update the database to version you wish.
* It must be greater than current DB version.
*/
public void patchTo(String versionSql, Version v)
{
//decorate resource with version filter
getCurrentDbVersion(versionSql);
DbScriptsResource filteredScripts =
VersionFilter
.filter(sqlScripts)
.inBetween(currentDbVersion, v);
executeScriptsFromResource(filteredScripts);
}
/**
* Inner method create exec
*/
private void executeScriptsFromResource(DbScriptsResource resource)
{
DbInstaller installer = new DbInstaller(dbConnection, variables);
installer.setEventListener(eventListener);
installer.setVersionFactory(versionFactory);
installer.setVersionUpdateSql(versionUpdateSqlScript);
installer.install(resource);
}
/**
* get and fill the current DB version value to property
*/
private void getCurrentDbVersion(String versionSql)
{
SqlScriptStrategy strategy = new SqlStatementStrategy(dbConnection, new ResultSetListener() {
@Override
public void onResultSet(ResultSet rs) throws SQLException {
rs.next();
String versionTxt = rs.getString(1);
currentDbVersion = versionFactory.parse(versionTxt);
}
});
SqlScriptLoader
.loadScriptAsText(versionSql)
.viaStrategy(VelocityDecorator.decorate(strategy, variables));
}
}