001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo.tools.ant;
012:
013: import com.versant.core.jdbc.sql.diff.ControlParams;
014: import com.versant.core.jdbc.sql.SqlDriver;
015: import com.versant.core.jdbc.JdbcStorageManagerFactory;
016: import com.versant.core.jdbc.JdbcConnectionSource;
017: import com.versant.core.common.config.ConfigParser;
018: import com.versant.core.common.config.ConfigInfo;
019: import com.versant.core.storagemanager.StorageManagerFactory;
020: import com.versant.core.storagemanager.StorageManagerFactoryBuilder;
021:
022: import java.util.Properties;
023: import java.util.ArrayList;
024: import java.util.Date;
025: import java.util.Iterator;
026: import java.io.*;
027: import java.text.SimpleDateFormat;
028: import java.sql.Connection;
029: import java.sql.SQLException;
030: import java.sql.Statement;
031:
032: /**
033: */
034: public class SchemaMigrationBean {
035: private ConfigInfo config;
036: private Properties properties;
037: private String propertiesResourceName = "versant.properties";
038: private File propertiesFile;
039:
040: private StorageManagerFactory smf;
041:
042: private ControlParams params = null;
043: private boolean checkLength = true;
044: private boolean checkType = true;
045: private boolean checkScale = true;
046: private boolean checkNulls = true;
047: private boolean checkPK = true;
048: private boolean checkIndex = true;
049: private boolean checkConstraint = true;
050: private boolean checkExtraColumns = true;
051: private File outputDir = null;
052: private boolean direct = false;
053: private String datastoreName;
054: private boolean logEventsToSysOut = true;
055: private PrintStream out = System.out;
056:
057: /**
058: * Returns the output PrintStream
059: */
060: public PrintStream getOut() {
061: return out;
062: }
063:
064: /**
065: * Set the logging PrintStream (System.out by default)
066: */
067: public void setOut(PrintStream out) {
068: this .out = out;
069: }
070:
071: public boolean isLogEventsToSysOut() {
072: return logEventsToSysOut;
073: }
074:
075: /**
076: * Must every thing be logged to System.out (true by default)
077: */
078: public void setLogEventsToSysOut(boolean logEventsToSysOut) {
079: this .logEventsToSysOut = logEventsToSysOut;
080: }
081:
082: /**
083: * Is the constraints of the tables checked.
084: */
085: public boolean isCheckConstraint() {
086: return checkConstraint;
087: }
088:
089: /**
090: * Must the constraints of the tables be checked.
091: */
092: public void setCheckConstraint(boolean checkConstraint) {
093: this .checkConstraint = checkConstraint;
094: }
095:
096: /**
097: * Is the extra columns of the table checked.
098: */
099: public boolean isCheckExtraColumns() {
100: return checkExtraColumns;
101: }
102:
103: /**
104: * Must the extra columns of the table be checked.
105: */
106: public void setCheckExtraColumns(boolean checkExtraColumns) {
107: this .checkExtraColumns = checkExtraColumns;
108: }
109:
110: /**
111: * Is the indexes of the tables checked.
112: */
113: public boolean isCheckIndex() {
114: return checkIndex;
115: }
116:
117: /**
118: * Must the indexes of the tables be checked.
119: */
120: public void setCheckIndex(boolean checkIndex) {
121: this .checkIndex = checkIndex;
122: }
123:
124: /**
125: * Is the lenght of the columns checked.
126: */
127: public boolean isCheckLength() {
128: return checkLength;
129: }
130:
131: /**
132: * Must the lenght of the columns be checked.
133: */
134: public void setCheckLength(boolean checkLength) {
135: this .checkLength = checkLength;
136: }
137:
138: /**
139: * Is the null values of the columns checked.
140: */
141: public boolean isCheckNulls() {
142: return checkNulls;
143: }
144:
145: /**
146: * Must the null values of the columns be checked.
147: */
148: public void setCheckNulls(boolean checkNulls) {
149: this .checkNulls = checkNulls;
150: }
151:
152: /**
153: * Is the primary keys of the tables checked.
154: */
155: public boolean isCheckPK() {
156: return checkPK;
157: }
158:
159: /**
160: * Must the primary keys of the tables be checked.
161: */
162: public void setCheckPK(boolean checkPK) {
163: this .checkPK = checkPK;
164: }
165:
166: /**
167: * Is the scale of the columns checked.
168: */
169: public boolean isCheckScale() {
170: return checkScale;
171: }
172:
173: /**
174: * Must the scale of the columns be checked.
175: */
176: public void setCheckScale(boolean checkScale) {
177: this .checkScale = checkScale;
178: }
179:
180: /**
181: * Is the types of the columns checked.
182: */
183: public boolean isCheckType() {
184: return checkType;
185: }
186:
187: /**
188: * Must the types of the columns be checked.
189: */
190: public void setCheckType(boolean checkType) {
191: this .checkType = checkType;
192: }
193:
194: /**
195: * Gets the datastore name.
196: */
197: public String getDatastoreName() {
198: return datastoreName;
199: }
200:
201: /**
202: * Sets the datastore name i.e. store0,
203: * if this property is not set, then the first datastore will be used.
204: */
205: public void setDatastoreName(String datastoreName) {
206: this .datastoreName = datastoreName;
207: }
208:
209: /**
210: * Will the script be executed directly.
211: */
212: public boolean isDirect() {
213: return direct;
214: }
215:
216: /**
217: * Must the script be generated and executed directly?
218: */
219: public void setDirect(boolean direct) {
220: this .direct = direct;
221: }
222:
223: public File getOutputDir() {
224: return outputDir;
225: }
226:
227: /**
228: * Where must the script be generated to.
229: */
230: public void setOutputDir(File outputDir) {
231: this .outputDir = outputDir;
232: }
233:
234: /**
235: * Get the jdogenie properties.
236: */
237:
238: public Properties getProperties() {
239: return properties;
240: }
241:
242: /**
243: * Sets the jdogenie properties, these are the same properies that are in
244: * the *.jdogenie properties file.
245: */
246: public void setProperties(Properties properties) {
247: if (config != null) {
248: // throw exe
249: }
250: this .properties = properties;
251: ConfigParser parser = new ConfigParser();
252: config = parser.parse(properties);
253: config.validate();
254: }
255:
256: /**
257: * Get the *.jdogenie property file
258: */
259: public File getPropertiesFile() {
260: return propertiesFile;
261: }
262:
263: /**
264: * Sets the *.jdogenie property as a file.
265: */
266: public void setPropertiesFile(File propertiesFile) {
267: if (config != null) {
268: // throw exe
269: }
270: this .propertiesFile = propertiesFile;
271: ConfigParser parser = new ConfigParser();
272: config = parser.parseResource(propertiesFile);
273: config.validate();
274: }
275:
276: /**
277: * Gets the *.jdogenie property file resource name
278: */
279: public String getPropertiesResourceName() {
280: return propertiesResourceName;
281: }
282:
283: /**
284: * Sets the *.jdogenie property filename as a resource.
285: */
286: public void setPropertiesResourceName(String propertiesResourceName) {
287: if (config != null) {
288: // throw exe
289: }
290: this .propertiesResourceName = propertiesResourceName;
291: ConfigParser parser = new ConfigParser();
292: config = parser.parseResource(propertiesResourceName);
293: config.validate();
294: }
295:
296: private void init() {
297:
298: Thread.currentThread().setContextClassLoader(getClassLoader());
299:
300: StorageManagerFactoryBuilder b = new StorageManagerFactoryBuilder();
301: b.setConfig(config);
302: b.setLoader(getClassLoader());
303: b.setFullInit(false);
304: smf = b.createStorageManagerFactory();
305:
306: params = new ControlParams();
307: params.setCheckConstraint(checkConstraint);
308: params.setCheckExtraColumns(checkExtraColumns);
309: params.setCheckIndex(checkIndex);
310: params.setCheckLength(checkLength);
311: params.setCheckNulls(checkNulls);
312: params.setCheckPK(checkPK);
313: params.setCheckScale(checkScale);
314: params.setCheckType(checkType);
315: }
316:
317: private ClassLoader getClassLoader() {
318: ClassLoader taskClassLoader = getClass().getClassLoader();
319: if (taskClassLoader == null) {
320: taskClassLoader = ClassLoader.getSystemClassLoader();
321: }
322: return taskClassLoader;
323: }
324:
325: /**
326: * Migrate the given database, if direct is 'false' then just a script is
327: * generated.
328: * if direct is true, the the script is executed against the given datastore.
329: */
330: public void migrateDatabase() throws Exception {
331: String script = generateScript();
332: if (direct) {
333: JdbcConnectionSource conSrc = ((JdbcStorageManagerFactory) smf)
334: .getConnectionSource();
335: Connection con = null;
336: try {
337: runScript(con, script);
338: } finally {
339: if (con != null) {
340: try {
341: conSrc.returnConnection(con);
342: } catch (SQLException e) {
343: // ignore
344: }
345: }
346: }
347: }
348: }
349:
350: private String getFileName(SqlDriver sqlDriver) {
351: SimpleDateFormat formatter = new SimpleDateFormat(
352: "dd-MM-yyyy_H-m");
353: return "Schema_Migration_" + sqlDriver.getName() + "_"
354: + formatter.format(new Date());
355: }
356:
357: /**
358: * Generate the schema migration script, if there is no changes then an empty
359: * String is returned.
360: *
361: */
362: public String generateScript() throws Exception {
363: init();
364: JdbcStorageManagerFactory jsmf = (JdbcStorageManagerFactory) smf;
365: JdbcConnectionSource conSrc = jsmf.getConnectionSource();
366: Connection con = null;
367: FileOutputStream fout = null;
368: PrintWriter out1 = null;
369: try {
370: if (logEventsToSysOut) {
371: out.println("Checking schema on " + conSrc.getURL());
372: }
373: StringWriter error = new StringWriter(10240);
374: PrintWriter perror = new PrintWriter(error, false);
375:
376: StringWriter fix = new StringWriter(10240);
377: PrintWriter pfix = new PrintWriter(fix, false);
378:
379: con = conSrc.getConnection(false, false);
380: boolean valid = jsmf.getSqlDriver().checkDDL(
381: jsmf.getJdbcMetaData().getTables(), con, perror,
382: pfix, params);
383: perror.close();
384: pfix.close();
385: if (valid) {
386: if (logEventsToSysOut) {
387: out.println("Schema is valid.");
388: }
389: return "";
390: } else {
391: if (logEventsToSysOut) {
392: out.println("Schema has errors.");
393: out.println(error.toString());
394: }
395: error.close();
396: if (outputDir != null) {
397: String fileName = getFileName(jsmf.getSqlDriver())
398: + ".sql";
399: if (logEventsToSysOut) {
400: out.println("Writing file (" + fileName
401: + ") to directory " + outputDir);
402: }
403: File f = new File(outputDir, fileName);
404: fout = new FileOutputStream(f);
405: out1 = new PrintWriter(fout);
406: out1.write(fix.toString());
407: out1.flush();
408: out1.close();
409: }
410: return fix.toString();
411: }
412: } finally {
413: if (con != null) {
414: try {
415: conSrc.returnConnection(con);
416: } catch (SQLException e) {
417: // ignore
418: }
419: }
420: }
421: }
422:
423: private void runScript(Connection con, String script)
424: throws Exception {
425: if (!con.getAutoCommit()) {
426: con.rollback();
427: con.setAutoCommit(true);
428: }
429:
430: SQLScriptParser shredder = new SQLScriptParser();
431: ArrayList list = shredder.parse(script, true);
432: for (Iterator iter = list.iterator(); iter.hasNext();) {
433: SQLScriptParser.SQLScriptPart scriptPart = (SQLScriptParser.SQLScriptPart) iter
434: .next();
435: if (logEventsToSysOut) {
436: out.println("Executing: \n" + scriptPart.getSql());
437: }
438: Statement stat = null;
439: try {
440: stat = con.createStatement();
441: stat.execute(scriptPart.getSql());
442: } finally {
443: try {
444: stat.close();
445: } catch (SQLException e) {
446: //hide
447: }
448: }
449: }
450: }
451:
452: /**
453: * Execute the given script agains the datastore
454: */
455: public void executeScript(String script) throws Exception {
456: init();
457: JdbcConnectionSource conSrc = ((JdbcStorageManagerFactory) smf)
458: .getConnectionSource();
459: Connection con = null;
460: try {
461: runScript(con, script);
462: } finally {
463: if (con != null) {
464: try {
465: conSrc.returnConnection(con);
466: } catch (SQLException e) {
467: // ignore
468: }
469: }
470: }
471: }
472:
473: }
|