001: /*
002: * JBoss, Home of Professional Open Source
003: * Copyright 2005, JBoss Inc., and individual contributors as indicated
004: * by the @authors tag. See the copyright.txt in the distribution for a
005: * full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jbpm.db;
023:
024: import java.io.File;
025: import java.io.FileInputStream;
026: import java.io.FileNotFoundException;
027: import java.io.FileOutputStream;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.io.PrintStream;
031: import java.io.Serializable;
032: import java.lang.reflect.Field;
033: import java.sql.Connection;
034: import java.sql.ResultSet;
035: import java.sql.SQLException;
036: import java.sql.Statement;
037: import java.util.ArrayList;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.Properties;
041:
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044: import org.hibernate.cfg.Configuration;
045: import org.hibernate.cfg.Environment;
046: import org.hibernate.connection.ConnectionProvider;
047: import org.hibernate.connection.ConnectionProviderFactory;
048: import org.hibernate.dialect.Dialect;
049: import org.hibernate.engine.Mapping;
050: import org.hibernate.mapping.ForeignKey;
051: import org.hibernate.mapping.Table;
052: import org.hibernate.tool.hbm2ddl.SchemaExport;
053: import org.hibernate.util.JDBCExceptionReporter;
054: import org.jbpm.JbpmException;
055:
056: /**
057: * utilities for the jBPM database schema.
058: */
059: public class JbpmSchema implements Serializable {
060:
061: private static final long serialVersionUID = 1L;
062:
063: static final String JBPM_TABLE_PREFIX = "JBPM_";
064:
065: Configuration configuration = null;
066: Properties properties = null;
067: Dialect dialect = null;
068: Mapping mapping = null;
069: String[] createSql = null;
070: String[] dropSql = null;
071: String[] cleanSql = null;
072:
073: ConnectionProvider connectionProvider = null;
074: Connection connection = null;
075: Statement statement = null;
076:
077: public JbpmSchema(Configuration configuration) {
078: this .configuration = configuration;
079: this .properties = configuration.getProperties();
080: this .dialect = Dialect.getDialect(properties);
081: try {
082: // get the mapping field via reflection :-(
083: Field mappingField = Configuration.class
084: .getDeclaredField("mapping");
085: mappingField.setAccessible(true);
086: this .mapping = (Mapping) mappingField.get(configuration);
087: } catch (Exception e) {
088: throw new JbpmException(
089: "couldn't get the hibernate mapping", e);
090: }
091: }
092:
093: public String[] getCreateSql() {
094: if (createSql == null) {
095: createSql = configuration
096: .generateSchemaCreationScript(dialect);
097: }
098: return createSql;
099: }
100:
101: public String[] getDropSql() {
102: if (dropSql == null) {
103: dropSql = configuration.generateDropSchemaScript(dialect);
104: }
105: return dropSql;
106: }
107:
108: public String[] getCleanSql() {
109: if (cleanSql == null) {
110: // loop over all foreign key constraints
111: List dropForeignKeysSql = new ArrayList();
112: List createForeignKeysSql = new ArrayList();
113: Iterator iter = configuration.getTableMappings();
114: while (iter.hasNext()) {
115: Table table = (Table) iter.next();
116: if (table.isPhysicalTable()) {
117: Iterator subIter = table.getForeignKeyIterator();
118: while (subIter.hasNext()) {
119: ForeignKey fk = (ForeignKey) subIter.next();
120: if (fk.isPhysicalConstraint()) {
121: // collect the drop foreign key constraint sql
122: dropForeignKeysSql
123: .add(fk
124: .sqlDropString(
125: dialect,
126: properties
127: .getProperty(Environment.DEFAULT_CATALOG),
128: properties
129: .getProperty(Environment.DEFAULT_SCHEMA)));
130: // and collect the create foreign key constraint sql
131: createForeignKeysSql
132: .add(fk
133: .sqlCreateString(
134: dialect,
135: mapping,
136: properties
137: .getProperty(Environment.DEFAULT_CATALOG),
138: properties
139: .getProperty(Environment.DEFAULT_SCHEMA)));
140: }
141: }
142: }
143: }
144:
145: List deleteSql = new ArrayList();
146: iter = configuration.getTableMappings();
147: while (iter.hasNext()) {
148: Table table = (Table) iter.next();
149: deleteSql.add("delete from " + table.getName());
150: }
151:
152: // glue
153: // - drop foreign key constraints
154: // - delete contents of all tables
155: // - create foreign key constraints
156: // together to form the clean script
157: List cleanSqlList = new ArrayList();
158: cleanSqlList.addAll(dropForeignKeysSql);
159: cleanSqlList.addAll(deleteSql);
160: cleanSqlList.addAll(createForeignKeysSql);
161:
162: cleanSql = (String[]) cleanSqlList
163: .toArray(new String[cleanSqlList.size()]);
164: }
165: return cleanSql;
166: }
167:
168: public boolean hasJbpmTables() {
169: return (getJbpmTables().size() > 0);
170: }
171:
172: public List getJbpmTables() {
173: // delete all the data in the jbpm tables
174: List jbpmTableNames = new ArrayList();
175: try {
176: createConnection();
177: ResultSet resultSet = connection.getMetaData().getTables(
178: null, null, null, null);
179: while (resultSet.next()) {
180: String tableName = resultSet.getString("TABLE_NAME");
181: if ((tableName != null)
182: && (tableName.length() > 5)
183: && (JBPM_TABLE_PREFIX
184: .equalsIgnoreCase(tableName.substring(
185: 0, 5)))) {
186: jbpmTableNames.add(tableName);
187: }
188: }
189: } catch (SQLException e) {
190: throw new JbpmException("couldn't get the jbpm table names");
191: } finally {
192: closeConnection();
193: }
194: return jbpmTableNames;
195: }
196:
197: public void dropSchema() {
198: execute(getDropSql());
199: }
200:
201: public void createSchema() {
202: execute(getCreateSql());
203: }
204:
205: public void cleanSchema() {
206: execute(getCleanSql());
207: }
208:
209: public void saveSqlScripts(String dir, String prefix) {
210: try {
211: new File(dir).mkdirs();
212: saveSqlScript(dir + "/" + prefix + ".drop.sql",
213: getDropSql());
214: saveSqlScript(dir + "/" + prefix + ".create.sql",
215: getCreateSql());
216: saveSqlScript(dir + "/" + prefix + ".clean.sql",
217: getCleanSql());
218: new SchemaExport(configuration).setDelimiter(
219: getSqlDelimiter()).setOutputFile(
220: dir + "/" + prefix + ".drop.create.sql").create(
221: true, false);
222: } catch (Exception e) {
223: throw new JbpmException("couldn't generate scripts", e);
224: }
225: }
226:
227: public static void main(String[] args) {
228: try {
229: if ((args == null) || (args.length == 0)) {
230: throw new IllegalArgumentException();
231: }
232:
233: String cmd = args[0];
234:
235: if ("create".equalsIgnoreCase(cmd)) {
236: Configuration configuration = createConfiguration(args,
237: 1);
238: new JbpmSchema(configuration).createSchema();
239: } else if ("drop".equalsIgnoreCase(cmd)) {
240: Configuration configuration = createConfiguration(args,
241: 1);
242: new JbpmSchema(configuration).dropSchema();
243: } else if ("clean".equalsIgnoreCase(cmd)) {
244: Configuration configuration = createConfiguration(args,
245: 1);
246: new JbpmSchema(configuration).cleanSchema();
247: } else if ("scripts".equalsIgnoreCase(cmd)) {
248: Configuration configuration = createConfiguration(args,
249: 3);
250: new JbpmSchema(configuration).saveSqlScripts(args[1],
251: args[2]);
252: }
253:
254: } catch (IllegalArgumentException e) {
255: System.err
256: .println("syntax: JbpmSchema create [<hibernate.cfg.xml> [<hibernate.properties>]]");
257: System.err
258: .println("syntax: JbpmSchema drop [<hibernate.cfg.xml> [<hibernate.properties>]]");
259: System.err
260: .println("syntax: JbpmSchema clean [<hibernate.cfg.xml> [<hibernate.properties>]]");
261: System.err
262: .println("syntax: JbpmSchema scripts <dir> <prefix> [<hibernate.cfg.xml> [<hibernate.properties>]]");
263: } catch (Exception e) {
264: e.printStackTrace();
265: throw new JbpmException(e);
266: }
267: }
268:
269: static Configuration createConfiguration(String[] args, int index) {
270: String hibernateCfgXml = (args.length > index ? args[index]
271: : "hibernate.cfg.xml");
272: String hibernateProperties = (args.length > (index + 1) ? args[index + 1]
273: : null);
274:
275: Configuration configuration = new Configuration();
276: configuration.configure(new File(hibernateCfgXml));
277: if (hibernateProperties != null) {
278: try {
279: Properties properties = new Properties();
280: InputStream inputStream = new FileInputStream(
281: hibernateProperties);
282: properties.load(inputStream);
283: configuration.setProperties(properties);
284: } catch (Exception e) {
285: e.printStackTrace();
286: throw new JbpmException(
287: "couldn't load hibernate configuration", e);
288: }
289: }
290:
291: return configuration;
292: }
293:
294: void saveSqlScript(String fileName, String[] sql)
295: throws FileNotFoundException {
296: FileOutputStream fileOutputStream = new FileOutputStream(
297: fileName);
298: try {
299: PrintStream printStream = new PrintStream(fileOutputStream);
300: for (int i = 0; i < sql.length; i++) {
301: printStream.println(sql[i] + getSqlDelimiter());
302: }
303: } finally {
304: try {
305: fileOutputStream.close();
306: } catch (IOException e) {
307: e.printStackTrace();
308: }
309: }
310: }
311:
312: public void execute(String[] sqls) {
313: String sql = null;
314: String showSqlText = properties
315: .getProperty("hibernate.show_sql");
316: boolean showSql = ("true".equalsIgnoreCase(showSqlText));
317:
318: try {
319: createConnection();
320: statement = connection.createStatement();
321:
322: for (int i = 0; i < sqls.length; i++) {
323: sql = sqls[i];
324:
325: if (showSql)
326: log.debug(sql);
327: statement.executeUpdate(sql);
328: }
329:
330: } catch (SQLException e) {
331: e.printStackTrace();
332: throw new JbpmException("couldn't execute sql '" + sql
333: + "'", e);
334: } finally {
335: closeConnection();
336: }
337: }
338:
339: void closeConnection() {
340: try {
341: if (statement != null)
342: statement.close();
343: if (connection != null) {
344: JDBCExceptionReporter.logWarnings(connection
345: .getWarnings());
346: connection.clearWarnings();
347: connectionProvider.closeConnection(connection);
348: connectionProvider.close();
349: }
350: } catch (Exception e) {
351: System.err.println("Could not close connection");
352: e.printStackTrace();
353: }
354: }
355:
356: void createConnection() throws SQLException {
357: connectionProvider = ConnectionProviderFactory
358: .newConnectionProvider(properties);
359: connection = connectionProvider.getConnection();
360: if (!connection.getAutoCommit()) {
361: connection.commit();
362: connection.setAutoCommit(true);
363: }
364: }
365:
366: public Properties getProperties() {
367: return properties;
368: }
369:
370: // sql delimiter ////////////////////////////////////////////////////////////
371:
372: static String sqlDelimiter = null;
373:
374: synchronized String getSqlDelimiter() {
375: if (sqlDelimiter == null) {
376: sqlDelimiter = properties.getProperty("jbpm.sql.delimiter",
377: ";");
378: }
379: return sqlDelimiter;
380: }
381:
382: // logger ///////////////////////////////////////////////////////////////////
383:
384: private static final Log log = LogFactory.getLog(JbpmSchema.class);
385: }
|