001: package com.technoetic.xplanner.db.hibernate;
002:
003: import com.technoetic.xplanner.XPlannerProperties;
004: import com.technoetic.xplanner.db.hsqldb.HsqlServer;
005: import net.sf.hibernate.HibernateException;
006: import net.sf.hibernate.cfg.Configuration;
007: import net.sf.hibernate.connection.ConnectionProvider;
008: import net.sf.hibernate.connection.ConnectionProviderFactory;
009: import net.sf.hibernate.dialect.Dialect;
010: import net.sf.hibernate.util.JDBCExceptionReporter;
011: import net.sf.hibernate.util.StringHelper;
012: import org.apache.commons.logging.Log;
013: import org.apache.commons.logging.LogFactory;
014:
015: import java.io.File;
016: import java.io.FileWriter;
017: import java.io.IOException;
018: import java.sql.Connection;
019: import java.sql.SQLException;
020: import java.sql.Statement;
021: import java.util.Properties;
022: import java.util.StringTokenizer;
023:
024: /**
025: * Modified version of Hibernate's SchemaExport to allow us to use XSL transformed mapping files. The
026: * original class couldn't be used because the execute() method is private.
027: */
028: public class TransformingSchemaExport {
029:
030: private static final Log log = LogFactory
031: .getLog(TransformingSchemaExport.class);
032:
033: private String[] dropSQL;
034: private String[] createSQL;
035: private Properties connectionProperties;
036: private String outputFile = null;
037: private Dialect dialect;
038: private String delimiter;
039:
040: /**
041: * Create a schema exporter for the given Configuration
042: */
043: public TransformingSchemaExport(Configuration cfg)
044: throws HibernateException {
045: this (cfg, cfg.getProperties());
046: }
047:
048: /**
049: * Create a schema exporter for the given Configuration, with the given
050: * database connection properties.
051: */
052: public TransformingSchemaExport(Configuration cfg,
053: Properties connectionProperties) throws HibernateException {
054: this .connectionProperties = connectionProperties;
055: this .dialect = Dialect.getDialect(connectionProperties);
056: this .dropSQL = cfg.generateDropSchemaScript(dialect);
057: this .createSQL = cfg.generateSchemaCreationScript(dialect);
058: }
059:
060: /**
061: * Set an output filename. The generated script will be written to this file.
062: */
063: public TransformingSchemaExport setOutputFile(String filename) {
064: outputFile = filename;
065: return this ;
066: }
067:
068: /**
069: * Set the end of statement delimiter
070: */
071: public TransformingSchemaExport setDelimiter(String delimiter) {
072: this .delimiter = delimiter;
073: return this ;
074: }
075:
076: /**
077: * Run the schema creation script.
078: * @param script print the DDL to the console
079: * @param export export the script to the database
080: */
081: public void create(boolean script, boolean export) throws Exception {
082: execute(script, export, false, true);
083: }
084:
085: /**
086: * Run the drop schema script.
087: * @param script print the DDL to the console
088: * @param export export the script to the database
089: */
090: public void drop(boolean script, boolean export) throws Exception {
091: execute(script, export, true, true);
092: }
093:
094: private void execute(boolean script, boolean export,
095: boolean justDrop, boolean format) throws Exception {
096:
097: log.info("Running hbm2ddl schema export");
098:
099: Connection connection = null;
100: FileWriter fileOutput = null;
101: ConnectionProvider connectionProvider = null;
102: Statement statement = null;
103:
104: Properties props = new Properties();
105: props.putAll(dialect.getDefaultProperties());
106: props.putAll(connectionProperties);
107:
108: try {
109:
110: if (outputFile != null) {
111: log.info("writing generated schema to file: "
112: + outputFile);
113: fileOutput = new FileWriter(outputFile);
114: }
115:
116: if (export) {
117: log.info("exporting generated schema to database");
118: connectionProvider = ConnectionProviderFactory
119: .newConnectionProvider(props);
120: connection = connectionProvider.getConnection();
121: if (!connection.getAutoCommit()) {
122: connection.commit();
123: connection.setAutoCommit(true);
124: }
125: statement = connection.createStatement();
126: }
127:
128: for (int i = 0; i < dropSQL.length; i++) {
129: try {
130: String formatted = dropSQL[i];
131: if (delimiter != null)
132: formatted += delimiter;
133: if (script)
134: System.out.println(formatted);
135: log.debug(formatted);
136: if (outputFile != null)
137: fileOutput.write(formatted + "\n");
138: if (export)
139: statement.executeUpdate(dropSQL[i]);
140: } catch (SQLException e) {
141: log.debug("Unsuccessful: " + dropSQL[i]);
142: log.debug(e.getMessage());
143: }
144:
145: }
146:
147: if (!justDrop) {
148: for (int j = 0; j < createSQL.length; j++) {
149: String formatted = format ? format(createSQL[j])
150: : createSQL[j];
151: if (delimiter != null)
152: formatted += delimiter;
153: if (script)
154: System.out.println(formatted);
155: log.debug(formatted);
156: if (outputFile != null)
157: fileOutput.write(formatted + "\n");
158: if (export)
159: statement.executeUpdate(createSQL[j]);
160: }
161: }
162:
163: log.info("schema export complete");
164:
165: } catch (Exception e) {
166: log.error("schema export unsuccessful", e);
167: throw e;
168: } finally {
169:
170: try {
171: if (statement != null)
172: statement.close();
173: if (connection != null) {
174: JDBCExceptionReporter.logWarnings(connection
175: .getWarnings());
176: connection.clearWarnings();
177: connectionProvider.closeConnection(connection);
178: connectionProvider.close();
179: }
180: } catch (Exception e) {
181: log.error("Could not close connection", e);
182: throw e;
183: }
184:
185: try {
186: if (fileOutput != null)
187: fileOutput.close();
188: } catch (IOException ioe) {
189: log.error("Error closing output file: " + outputFile,
190: ioe);
191: throw ioe;
192: }
193:
194: }
195: }
196:
197: /**
198: * Format an SQL statement using simple rules:
199: * a) Insert newline after each comma;
200: * b) Indent three spaces after each inserted newline;
201: * If the statement contains single/double quotes return unchanged,
202: * it is too complex and could be broken by simple formatting.
203: */
204: private static String format(String sql) {
205:
206: if (sql.indexOf("\"") > 0 || sql.indexOf("'") > 0) {
207: return sql;
208: }
209:
210: String formatted;
211:
212: if (sql.toLowerCase().startsWith("create table")) {
213:
214: StringBuffer result = new StringBuffer();
215: StringTokenizer tokens = new StringTokenizer(sql, "(,)",
216: true);
217:
218: int depth = 0;
219:
220: while (tokens.hasMoreTokens()) {
221: String tok = tokens.nextToken();
222: if (StringHelper.CLOSE_PAREN.equals(tok)) {
223: depth--;
224: if (depth == 0)
225: result.append("\n");
226: }
227: result.append(tok);
228: if (StringHelper.COMMA.equals(tok) && depth == 1)
229: result.append("\n ");
230: if (StringHelper.OPEN_PAREN.equals(tok)) {
231: depth++;
232: if (depth == 1)
233: result.append("\n ");
234: }
235: }
236:
237: formatted = result.toString();
238:
239: } else {
240: formatted = sql;
241: }
242:
243: return formatted;
244: }
245:
246: public static void main(String[] args) throws Throwable {
247: try {
248: Configuration cfg = HibernateHelper
249: .initializeConfiguration();
250:
251: boolean script = true;
252: boolean drop = false;
253: boolean export = true;
254: String outFile = null;
255: boolean formatSQL = false;
256: String delim = null;
257:
258: for (int i = 0; i < args.length; i++) {
259: if (args[i].startsWith("--")) {
260: if (args[i].equals("--quiet")) {
261: script = false;
262: } else if (args[i].equals("--drop")) {
263: drop = true;
264: } else if (args[i].equals("--text")) {
265: export = false;
266: } else if (args[i].startsWith("--output=")) {
267: outFile = args[i].substring(9);
268: } else if (args[i].equals("--format")) {
269: formatSQL = true;
270: } else if (args[i].startsWith("--delimiter=")) {
271: delim = args[i].substring(12);
272: } else if (args[i].startsWith("--config=")) {
273: cfg.configure(args[i].substring(9));
274: }
275: } else {
276: String filename = args[i];
277: if (filename.endsWith(".jar")) {
278: cfg.addJar(new File(filename));
279: } else {
280: cfg.addFile(filename);
281: }
282: }
283:
284: }
285: new TransformingSchemaExport(cfg, getProperties())
286: .setOutputFile(outFile).setDelimiter(delim)
287: .execute(script, export, drop, formatSQL);
288: } catch (Throwable e) {
289: log.error("Error creating schema ", e);
290: throw e;
291: } finally {
292: HsqlServer.shutdown();
293: }
294: }
295:
296: private static Properties getProperties() {
297: return new XPlannerProperties().get();
298: }
299: }
|