001// Copyright 2011 Leo Przybylski. All rights reserved. 002// 003// Redistribution and use in source and binary forms, with or without modification, are 004// permitted provided that the following conditions are met: 005// 006// 1. Redistributions of source code must retain the above copyright notice, this list of 007// conditions and the following disclaimer. 008// 009// 2. Redistributions in binary form must reproduce the above copyright notice, this list 010// of conditions and the following disclaimer in the documentation and/or other materials 011// provided with the distribution. 012// 013// THIS SOFTWARE IS PROVIDED BY Leo Przybylski ''AS IS'' AND ANY EXPRESS OR IMPLIED 014// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 015// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR 016// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 017// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 018// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 019// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 020// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 021// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 022// 023// The views and conclusions contained in the software and documentation are those of the 024// authors and should not be interpreted as representing official policies, either expressed 025// or implied, of Leo Przybylski. 026package org.kualigan.tools.ant.tasks; 027 028import org.apache.tools.ant.taskdefs.Jar; 029import org.apache.tools.ant.types.FileSet; 030 031import liquibase.CatalogAndSchema; 032import liquibase.Liquibase; 033import liquibase.resource.CompositeResourceAccessor; 034import liquibase.resource.FileSystemResourceAccessor; 035import liquibase.resource.ResourceAccessor; 036import liquibase.integration.ant.AntResourceAccessor; 037import liquibase.integration.ant.BaseLiquibaseTask; 038import liquibase.database.Database; 039import liquibase.database.DatabaseFactory; 040import liquibase.database.core.H2Database; 041import liquibase.database.jvm.JdbcConnection; 042import liquibase.structure.DatabaseObject; 043import liquibase.snapshot.DatabaseSnapshot; 044import liquibase.snapshot.SnapshotControl; 045import liquibase.snapshot.SnapshotGeneratorFactory; 046import org.apache.tools.ant.BuildException; 047 048import org.h2.tools.Backup; 049import org.h2.tools.DeleteDbFiles; 050 051import liquibase.ext.kualigan.diff.DiffGenerator; 052import liquibase.diff.DiffGeneratorFactory; 053import liquibase.diff.DiffResult; 054import liquibase.diff.compare.CompareControl; 055 056import java.io.File; 057import java.io.PrintStream; 058 059import java.sql.Connection; 060import java.sql.DriverManager; 061import java.sql.Statement; 062 063import java.util.Arrays; 064import java.util.HashSet; 065import java.util.Set; 066 067import static org.apache.tools.ant.Project.MSG_DEBUG; 068 069/** 070 * 071 * @author Leo Przybylski (przybyls@arizona.edu) 072 */ 073public class GenerateChangeLog extends BaseLiquibaseTask { 074 private String source; 075 private String target; 076 private boolean stateSaved; 077 078 public GenerateChangeLog() { } 079 080 public boolean isStateSaved() { 081 return stateSaved; 082 } 083 084 public void setStateSaved(boolean ss) { 085 stateSaved = ss; 086 } 087 088 public void setSource(String refid) { 089 this.source = refid; 090 } 091 092 public String getSource() { 093 return this.source; 094 } 095 096 public void setTarget(String refid) { 097 this.target = refid; 098 } 099 100 public String getTarget() { 101 return this.target; 102 } 103 104 @Override 105 protected void executeWithLiquibaseClassloader() throws BuildException { 106 final RdbmsConfig source = (RdbmsConfig) getProject().getReference(getSource()); 107 final RdbmsConfig target = (RdbmsConfig) getProject().getReference(getTarget()); 108 Database lbSource = null; 109 Database lbTarget = null; 110 final DatabaseFactory factory = DatabaseFactory.getInstance(); 111 try { 112 lbSource = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("source"))); 113 lbSource.setDefaultSchemaName(source.getSchema()); 114 lbTarget = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("target"))); 115 lbTarget.setDefaultSchemaName(target.getSchema()); 116 117 exportSchema(lbSource, lbTarget); 118 if (isStateSaved()) { 119 exportData(lbSource, lbTarget); 120 } 121 122 if (lbTarget instanceof H2Database) { 123 final Statement st = ((JdbcConnection) lbTarget.getConnection()).createStatement(); 124 st.execute("SHUTDOWN DEFRAG"); 125 } 126 127 } catch (Exception e) { 128 throw new BuildException(e); 129 } finally { 130 try { 131 if (lbSource != null) { 132 lbSource.close(); 133 } 134 if (lbTarget != null) { 135 lbTarget.close(); 136 } 137 } 138 catch (Exception e) { 139 } 140 } 141 142 if (isStateSaved()) { 143 log("Starting data load from schema " + source.getSchema()); 144 MigrateData migrateTask = new MigrateData(); 145 migrateTask.bindToOwner(this); 146 migrateTask.init(); 147 migrateTask.setSource(getSource()); 148 migrateTask.setTarget("h2"); 149 migrateTask.execute(); 150 try { 151 Backup.execute("work/export/data.zip", "work/export", "", true); 152 153 // delete the old database files 154 DeleteDbFiles.execute("split:22:work/export", "data", true); 155 } 156 catch (Exception e) { 157 throw new BuildException(e); 158 } 159 } 160 } 161 162 protected void exportSchema(final Database source, final Database target) { 163 try { 164 exportTables(source, target); 165 exportSequences(source, target); 166 exportViews(source, target); 167 exportIndexes(source, target); 168 exportConstraints(source, target); 169 } 170 catch (Exception e) { 171 throw new BuildException(e); 172 } 173 } 174 175 protected void export(final Database source, 176 final Database target, 177 final String snapshotTypes, 178 final String suffix) throws Exception { 179 final CatalogAndSchema catalogAndSchema = source.getDefaultSchema(); 180 final SnapshotControl snapshotControl = new SnapshotControl(source, snapshotTypes); 181 final CompareControl compareControl = new CompareControl(new CompareControl.SchemaComparison[]{new CompareControl.SchemaComparison(catalogAndSchema, catalogAndSchema)}, snapshotTypes); 182 // compareControl.addStatusListener(new OutDiffStatusListener()); 183 184 final DatabaseSnapshot referenceSnapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(compareControl.getSchemas(CompareControl.DatabaseRole.REFERENCE), source, snapshotControl); 185 final DatabaseSnapshot comparisonSnapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(compareControl.getSchemas(CompareControl.DatabaseRole.REFERENCE), target, snapshotControl); 186 // diff.setDiffTypes(snapshotTypes); 187 188 final DiffResult results = DiffGeneratorFactory.getInstance().compare(referenceSnapshot, comparisonSnapshot, compareControl); 189 // results.printChangeLog(getChangeLogFile() + suffix, target); 190 } 191 192 protected void exportConstraints(final Database source, final Database target) throws Exception { 193 export(source, target, "foreignKeys", "-cst.xml"); 194 } 195 196 protected void exportIndexes(final Database source, final Database target) throws Exception { 197 export(source, target, "indexes", "-idx.xml"); 198 } 199 200 protected void exportViews(final Database source, final Database target) throws Exception { 201 export(source, target, "views", "-vw.xml"); 202 } 203 204 protected void exportTables(final Database source, final Database target) throws Exception { 205 export(source, target, "tables, primaryKeys, uniqueConstraints", "-tab.xml"); 206 } 207 208 protected void exportSequences(final Database source, final Database target) throws Exception { 209 export(source, target, "sequences", "-seq.xml"); 210 } 211 212 213 private void exportData(final Database source, final Database target) { 214 Database h2db = null; 215 RdbmsConfig h2Config = new RdbmsConfig(); 216 h2Config.setDriver("org.h2.Driver"); 217 h2Config.setUrl("jdbc:h2:split:22:work/export/data"); 218 h2Config.setUsername("SA"); 219 h2Config.setPassword(""); 220 h2Config.setSchema("PUBLIC"); 221 getProject().addReference("h2", h2Config); 222 223 final DatabaseFactory factory = DatabaseFactory.getInstance(); 224 try { 225 h2db = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("h2"))); 226 h2db.setDefaultSchemaName(h2Config.getSchema()); 227 228 // export(new Diff(source, getDefaultSchemaName()), h2db, "tables", "-dat.xml"); 229 230 ResourceAccessor antFO = new AntResourceAccessor(getProject(), classpath); 231 ResourceAccessor fsFO = new FileSystemResourceAccessor(); 232 233 String changeLogFile = getChangeLogFile() + "-dat.xml"; 234 235 Liquibase liquibase = new Liquibase(changeLogFile, new CompositeResourceAccessor(antFO, fsFO), h2db); 236 237 log("Loading Schema"); 238 liquibase.update(getContexts()); 239 log("Finished Loading the Schema"); 240 241 } 242 catch (Exception e) { 243 throw new BuildException(e); 244 } 245 finally { 246 try { 247 if (h2db != null) { 248 // hsqldb.getConnection().createStatement().execute("SHUTDOWN"); 249 log("Closing h2 database"); 250 h2db.close(); 251 } 252 } 253 catch (Exception e) { 254 if (!(e instanceof java.sql.SQLNonTransientConnectionException)) { 255 e.printStackTrace(); 256 } 257 } 258 259 } 260 } 261 262 private void debug(String msg) { 263 log(msg, MSG_DEBUG); 264 } 265 266 private Connection openSource() { 267 return openConnection(getSource()); 268 } 269 270 private Connection openTarget() { 271 return openConnection(getTarget()); 272 } 273 274 private Connection openConnection(String reference) { 275 final RdbmsConfig config = (RdbmsConfig) getProject().getReference(reference); 276 return openConnection(config); 277 } 278 279 280 281 private Connection openConnection(RdbmsConfig config) { 282 Connection retval = null; 283 int retry_count = 0; 284 final int max_retry = 5; 285 while (retry_count < max_retry) { 286 try { 287 debug("Loading schema " + config.getSchema() + " at url " + config.getUrl()); 288 Class.forName(config.getDriver()); 289 retval = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()); 290 retval.setAutoCommit(true); 291 } 292 catch (Exception e) { 293 if (!e.getMessage().contains("Database lock acquisition failure") && !(e instanceof NullPointerException)) { 294 throw new BuildException(e); 295 } 296 } 297 finally { 298 retry_count++; 299 } 300 } 301 return retval; 302 } 303}