/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jbpm.db;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.connection.ConnectionProviderFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.Mapping;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Table;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.hibernate.util.JDBCExceptionReporter;
import org.jbpm.JbpmException;
/**
* utilities for the jBPM database schema.
*/
public class JbpmSchema implements Serializable {
private static final long serialVersionUID = 1L;
static final String JBPM_TABLE_PREFIX = "JBPM_";
Configuration configuration = null;
Properties properties = null;
Dialect dialect = null;
Mapping mapping = null;
String[] createSql = null;
String[] dropSql = null;
String[] cleanSql = null;
ConnectionProvider connectionProvider = null;
Connection connection = null;
Statement statement = null;
public JbpmSchema(Configuration configuration) {
this.configuration = configuration;
this.properties = configuration.getProperties();
this.dialect = Dialect.getDialect(properties);
try {
// get the mapping field via reflection :-(
Field mappingField = Configuration.class.getDeclaredField("mapping");
mappingField.setAccessible(true);
this.mapping = (Mapping) mappingField.get(configuration);
} catch (Exception e) {
throw new JbpmException("couldn't get the hibernate mapping", e);
}
}
public String[] getCreateSql() {
if (createSql==null) {
createSql = configuration.generateSchemaCreationScript(dialect);
}
return createSql;
}
public String[] getDropSql() {
if (dropSql==null) {
dropSql = configuration.generateDropSchemaScript(dialect);
}
return dropSql;
}
public String[] getCleanSql() {
if (cleanSql==null) {
// loop over all foreign key constraints
List dropForeignKeysSql = new ArrayList();
List createForeignKeysSql = new ArrayList();
Iterator iter = configuration.getTableMappings();
while ( iter.hasNext() ) {
Table table = ( Table ) iter.next();
if ( table.isPhysicalTable() ) {
Iterator subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) {
ForeignKey fk = ( ForeignKey ) subIter.next();
if ( fk.isPhysicalConstraint() ) {
// collect the drop foreign key constraint sql
dropForeignKeysSql.add( fk.sqlDropString(
dialect,
properties.getProperty(Environment.DEFAULT_CATALOG),
properties.getProperty(Environment.DEFAULT_SCHEMA) ) );
// and collect the create foreign key constraint sql
createForeignKeysSql.add( fk.sqlCreateString(
dialect,
mapping,
properties.getProperty(Environment.DEFAULT_CATALOG),
properties.getProperty(Environment.DEFAULT_SCHEMA) ) );
}
}
}
}
List deleteSql = new ArrayList();
iter = configuration.getTableMappings();
while (iter.hasNext()) {
Table table = (Table) iter.next();
deleteSql.add("delete from "+table.getName());
}
// glue
// - drop foreign key constraints
// - delete contents of all tables
// - create foreign key constraints
// together to form the clean script
List cleanSqlList = new ArrayList();
cleanSqlList.addAll(dropForeignKeysSql);
cleanSqlList.addAll(deleteSql);
cleanSqlList.addAll(createForeignKeysSql);
cleanSql = (String[]) cleanSqlList.toArray(new String[cleanSqlList.size()]);
}
return cleanSql;
}
public boolean hasJbpmTables() {
return (getJbpmTables().size()>0);
}
public List getJbpmTables() {
// delete all the data in the jbpm tables
List jbpmTableNames = new ArrayList();
try {
createConnection();
ResultSet resultSet = connection.getMetaData().getTables(null, null, null, null);
while(resultSet.next()) {
String tableName = resultSet.getString("TABLE_NAME");
if ( (tableName!=null)
&& (tableName.length()>5)
&& (JBPM_TABLE_PREFIX.equalsIgnoreCase(tableName.substring(0,5))) ) {
jbpmTableNames.add(tableName);
}
}
} catch (SQLException e) {
throw new JbpmException("couldn't get the jbpm table names");
} finally {
closeConnection();
}
return jbpmTableNames;
}
public void dropSchema() {
execute( getDropSql() );
}
public void createSchema() {
execute( getCreateSql() );
}
public void cleanSchema() {
execute( getCleanSql() );
}
public void saveSqlScripts(String dir, String prefix) {
try {
new File(dir).mkdirs();
saveSqlScript(dir+"/"+prefix+".drop.sql", getDropSql());
saveSqlScript(dir+"/"+prefix+".create.sql", getCreateSql());
saveSqlScript(dir+"/"+prefix+".clean.sql", getCleanSql());
new SchemaExport(configuration)
.setDelimiter(getSqlDelimiter())
.setOutputFile(dir+"/"+prefix+".drop.create.sql")
.create(true, false);
} catch (Exception e) {
throw new JbpmException("couldn't generate scripts", e);
}
}
public static void main(String[] args) {
try {
if ( (args==null) || (args.length==0) ) {
throw new IllegalArgumentException();
}
String cmd = args[0];
if ("create".equalsIgnoreCase(cmd)) {
Configuration configuration = createConfiguration(args, 1);
new JbpmSchema(configuration).createSchema();
} else if ("drop".equalsIgnoreCase(cmd)) {
Configuration configuration = createConfiguration(args, 1);
new JbpmSchema(configuration).dropSchema();
} else if ("clean".equalsIgnoreCase(cmd)) {
Configuration configuration = createConfiguration(args, 1);
new JbpmSchema(configuration).cleanSchema();
} else if ("scripts".equalsIgnoreCase(cmd)) {
Configuration configuration = createConfiguration(args, 3);
new JbpmSchema(configuration).saveSqlScripts(args[1], args[2]);
}
} catch (IllegalArgumentException e) {
System.err.println("syntax: JbpmSchema create [<hibernate.cfg.xml> [<hibernate.properties>]]");
System.err.println("syntax: JbpmSchema drop [<hibernate.cfg.xml> [<hibernate.properties>]]");
System.err.println("syntax: JbpmSchema clean [<hibernate.cfg.xml> [<hibernate.properties>]]");
System.err.println("syntax: JbpmSchema scripts <dir> <prefix> [<hibernate.cfg.xml> [<hibernate.properties>]]");
} catch (Exception e) {
e.printStackTrace();
throw new JbpmException(e);
}
}
static Configuration createConfiguration(String[] args, int index) {
String hibernateCfgXml = (args.length>index ? args[index] : "hibernate.cfg.xml");
String hibernateProperties = (args.length>(index+1) ? args[index+1] : null);
Configuration configuration = new Configuration();
configuration.configure(new File(hibernateCfgXml));
if (hibernateProperties!=null) {
try {
Properties properties = new Properties();
InputStream inputStream = new FileInputStream(hibernateProperties);
properties.load(inputStream);
configuration.setProperties(properties);
} catch (Exception e) {
e.printStackTrace();
throw new JbpmException("couldn't load hibernate configuration", e);
}
}
return configuration;
}
void saveSqlScript(String fileName, String[] sql) throws FileNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream(fileName);
try {
PrintStream printStream = new PrintStream(fileOutputStream);
for (int i=0; i<sql.length; i++) {
printStream.println(sql[i]+getSqlDelimiter());
}
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void execute(String[] sqls) {
String sql = null;
String showSqlText = properties.getProperty("hibernate.show_sql");
boolean showSql = ("true".equalsIgnoreCase(showSqlText));
try {
createConnection();
statement = connection.createStatement();
for (int i=0; i<sqls.length; i++) {
sql = sqls[i];
if (showSql) log.debug(sql);
statement.executeUpdate(sql);
}
} catch (SQLException e) {
e.printStackTrace();
throw new JbpmException("couldn't execute sql '"+sql+"'", e);
} finally {
closeConnection();
}
}
void closeConnection() {
try {
if (statement!=null) statement.close();
if (connection!=null) {
JDBCExceptionReporter.logWarnings( connection.getWarnings() );
connection.clearWarnings();
connectionProvider.closeConnection(connection);
connectionProvider.close();
}
}
catch(Exception e) {
System.err.println( "Could not close connection" );
e.printStackTrace();
}
}
void createConnection() throws SQLException {
connectionProvider = ConnectionProviderFactory.newConnectionProvider(properties);
connection = connectionProvider.getConnection();
if ( !connection.getAutoCommit() ) {
connection.commit();
connection.setAutoCommit(true);
}
}
public Properties getProperties() {
return properties;
}
// sql delimiter ////////////////////////////////////////////////////////////
static String sqlDelimiter = null;
synchronized String getSqlDelimiter() {
if (sqlDelimiter==null) {
sqlDelimiter = properties.getProperty("jbpm.sql.delimiter", ";");
}
return sqlDelimiter;
}
// logger ///////////////////////////////////////////////////////////////////
private static final Log log = LogFactory.getLog(JbpmSchema.class);
}