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.resource.CompositeResourceAccessor; 032import liquibase.resource.FileSystemResourceAccessor; 033import liquibase.resource.ResourceAccessor; 034import liquibase.Liquibase; 035import liquibase.integration.ant.AntResourceAccessor; 036import liquibase.integration.ant.BaseLiquibaseTask; 037import liquibase.database.Database; 038import liquibase.database.DatabaseFactory; 039import liquibase.database.core.H2Database; 040import liquibase.database.jvm.JdbcConnection; 041import org.apache.tools.ant.BuildException; 042 043import org.h2.tools.Backup; 044import org.h2.tools.DeleteDbFiles; 045 046import org.kualigan.tools.liquibase.Diff; 047import org.kualigan.tools.liquibase.DiffResult; 048 049import java.io.File; 050import java.io.PrintStream; 051 052import java.sql.Connection; 053import java.sql.DriverManager; 054import java.sql.Statement; 055 056import static org.apache.tools.ant.Project.MSG_DEBUG; 057 058/** 059 * 060 * @author Leo Przybylski (przybyls@arizona.edu) 061 */ 062public class GenerateChangeLog extends BaseLiquibaseTask { 063 private String source; 064 private String target; 065 private boolean stateSaved; 066 067 public GenerateChangeLog() { } 068 069 public boolean isStateSaved() { 070 return stateSaved; 071 } 072 073 public void setStateSaved(boolean ss) { 074 stateSaved = ss; 075 } 076 077 public void setSource(String refid) { 078 this.source = refid; 079 } 080 081 public String getSource() { 082 return this.source; 083 } 084 085 public void setTarget(String refid) { 086 this.target = refid; 087 } 088 089 public String getTarget() { 090 return this.target; 091 } 092 093 public void execute() { 094 final RdbmsConfig source = (RdbmsConfig) getProject().getReference(getSource()); 095 final RdbmsConfig target = (RdbmsConfig) getProject().getReference(getTarget()); 096 Database lbSource = null; 097 Database lbTarget = null; 098 final DatabaseFactory factory = DatabaseFactory.getInstance(); 099 try { 100 lbSource = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("source"))); 101 lbSource.setDefaultSchemaName(source.getSchema()); 102 lbTarget = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("target"))); 103 lbTarget.setDefaultSchemaName(target.getSchema()); 104 105 exportSchema(lbSource, lbTarget); 106 if (isStateSaved()) { 107 exportData(lbSource, lbTarget); 108 } 109 110 if (lbTarget instanceof H2Database) { 111 final Statement st = ((JdbcConnection) lbTarget.getConnection()).createStatement(); 112 st.execute("SHUTDOWN DEFRAG"); 113 } 114 115 } catch (Exception e) { 116 throw new BuildException(e); 117 } finally { 118 try { 119 if (lbSource != null) { 120 lbSource.close(); 121 } 122 if (lbTarget != null) { 123 lbTarget.close(); 124 } 125 } 126 catch (Exception e) { 127 } 128 } 129 130 if (isStateSaved()) { 131 log("Starting data load from schema " + source.getSchema()); 132 MigrateData migrateTask = new MigrateData(); 133 migrateTask.bindToOwner(this); 134 migrateTask.init(); 135 migrateTask.setSource(getSource()); 136 migrateTask.setTarget("h2"); 137 migrateTask.execute(); 138 try { 139 Backup.execute("work/export/data.zip", "work/export", "", true); 140 141 // delete the old database files 142 DeleteDbFiles.execute("split:22:work/export", "data", true); 143 } 144 catch (Exception e) { 145 throw new BuildException(e); 146 } 147 } 148 } 149 150 protected void exportSchema(Database source, Database target) { 151 try { 152 Diff diff = new Diff(source, source.getDefaultSchemaName()); 153 exportTables(diff, target); 154 exportSequences(diff, target); 155 exportViews(diff, target); 156 exportIndexes(diff, target); 157 exportConstraints(diff, target); 158 } 159 catch (Exception e) { 160 throw new BuildException(e); 161 } 162 } 163 164 protected void export(Diff diff, Database target, String diffTypes, String suffix) { 165 diff.setDiffTypes(diffTypes); 166 167 try { 168 DiffResult results = diff.compare(); 169 results.printChangeLog(getChangeLogFile() + suffix, target); 170 } 171 catch (Exception e) { 172 throw new BuildException(e); 173 } 174 } 175 176 protected void exportConstraints(Diff diff, Database target) { 177 export(diff, target, "foreignKeys", "-cst.xml"); 178 } 179 180 protected void exportIndexes(Diff diff, Database target) { 181 export(diff, target, "indexes", "-idx.xml"); 182 } 183 184 protected void exportViews(Diff diff, Database target) { 185 export(diff, target, "views", "-vw.xml"); 186 } 187 188 protected void exportTables(Diff diff, Database target) { 189 export(diff, target, "tables, primaryKeys, uniqueConstraints", "-tab.xml"); 190 } 191 192 protected void exportSequences(Diff diff, Database target) { 193 export(diff, target, "sequences", "-seq.xml"); 194 } 195 196 197 private void exportData(Database source, Database target) { 198 Database h2db = null; 199 RdbmsConfig h2Config = new RdbmsConfig(); 200 h2Config.setDriver("org.h2.Driver"); 201 h2Config.setUrl("jdbc:h2:split:22:work/export/data"); 202 h2Config.setUsername("SA"); 203 h2Config.setPassword(""); 204 h2Config.setSchema("PUBLIC"); 205 getProject().addReference("h2", h2Config); 206 207 final DatabaseFactory factory = DatabaseFactory.getInstance(); 208 try { 209 h2db = factory.findCorrectDatabaseImplementation(new JdbcConnection(openConnection("h2"))); 210 h2db.setDefaultSchemaName(h2Config.getSchema()); 211 212 export(new Diff(source, getDefaultSchemaName()), h2db, "tables", "-dat.xml"); 213 214 ResourceAccessor antFO = new AntResourceAccessor(getProject(), classpath); 215 ResourceAccessor fsFO = new FileSystemResourceAccessor(); 216 217 String changeLogFile = getChangeLogFile() + "-dat.xml"; 218 219 Liquibase liquibase = new Liquibase(changeLogFile, new CompositeResourceAccessor(antFO, fsFO), h2db); 220 221 log("Loading Schema"); 222 liquibase.update(getContexts()); 223 log("Finished Loading the Schema"); 224 225 } 226 catch (Exception e) { 227 throw new BuildException(e); 228 } 229 finally { 230 try { 231 if (h2db != null) { 232 // hsqldb.getConnection().createStatement().execute("SHUTDOWN"); 233 log("Closing h2 database"); 234 h2db.close(); 235 } 236 } 237 catch (Exception e) { 238 if (!(e instanceof java.sql.SQLNonTransientConnectionException)) { 239 e.printStackTrace(); 240 } 241 } 242 243 } 244 } 245 246 private void debug(String msg) { 247 log(msg, MSG_DEBUG); 248 } 249 250 private Connection openSource() { 251 return openConnection(getSource()); 252 } 253 254 private Connection openTarget() { 255 return openConnection(getTarget()); 256 } 257 258 private Connection openConnection(String reference) { 259 final RdbmsConfig config = (RdbmsConfig) getProject().getReference(reference); 260 return openConnection(config); 261 } 262 263 264 265 private Connection openConnection(RdbmsConfig config) { 266 Connection retval = null; 267 int retry_count = 0; 268 final int max_retry = 5; 269 while (retry_count < max_retry) { 270 try { 271 debug("Loading schema " + config.getSchema() + " at url " + config.getUrl()); 272 Class.forName(config.getDriver()); 273 retval = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword()); 274 retval.setAutoCommit(true); 275 } 276 catch (Exception e) { 277 if (!e.getMessage().contains("Database lock acquisition failure") && !(e instanceof NullPointerException)) { 278 throw new BuildException(e); 279 } 280 } 281 finally { 282 retry_count++; 283 } 284 } 285 return retval; 286 } 287}