Coverage Report - liquibase.integration.commandline.Main
 
Classes in this File Line Coverage Branch Coverage Complexity
Main
46%
223/484
31%
96/304
9.652
Main$1
100%
2/2
N/A
9.652
Main$2
0%
0/2
N/A
9.652
 
 1  
 package liquibase.integration.commandline;
 2  
 
 3  
 import java.io.BufferedInputStream;
 4  
 import java.io.BufferedOutputStream;
 5  
 import java.io.File;
 6  
 import java.io.FileInputStream;
 7  
 import java.io.FileOutputStream;
 8  
 import java.io.IOException;
 9  
 import java.io.InputStream;
 10  
 import java.io.OutputStreamWriter;
 11  
 import java.io.PrintStream;
 12  
 import java.io.Writer;
 13  
 import java.lang.reflect.Field;
 14  
 import java.net.URL;
 15  
 import java.net.URLClassLoader;
 16  
 import java.security.AccessController;
 17  
 import java.security.PrivilegedAction;
 18  
 import java.text.DateFormat;
 19  
 import java.text.ParseException;
 20  
 import java.text.SimpleDateFormat;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Arrays;
 23  
 import java.util.Enumeration;
 24  
 import java.util.HashMap;
 25  
 import java.util.LinkedHashSet;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 import java.util.Properties;
 29  
 import java.util.Set;
 30  
 import java.util.jar.JarEntry;
 31  
 import java.util.jar.JarFile;
 32  
 
 33  
 import liquibase.Liquibase;
 34  
 import liquibase.database.Database;
 35  
 import liquibase.exception.CommandLineParsingException;
 36  
 import liquibase.exception.DatabaseException;
 37  
 import liquibase.exception.ValidationFailedException;
 38  
 import liquibase.lockservice.LockService;
 39  
 import liquibase.logging.LogFactory;
 40  
 import liquibase.logging.LogLevel;
 41  
 import liquibase.logging.Logger;
 42  
 import liquibase.resource.ClassLoaderResourceAccessor;
 43  
 import liquibase.resource.CompositeResourceAccessor;
 44  
 import liquibase.resource.FileSystemResourceAccessor;
 45  
 import liquibase.servicelocator.ServiceLocator;
 46  
 import liquibase.util.LiquibaseUtil;
 47  
 import liquibase.util.StreamUtil;
 48  
 import liquibase.util.StringUtils;
 49  
 
 50  
 /**
 51  
  * Class for executing Liquibase via the command line.
 52  
  */
 53  
 public class Main {
 54  
     protected ClassLoader classLoader;
 55  
 
 56  
     protected String driver;
 57  
     protected String username;
 58  
     protected String password;
 59  
     protected String url;
 60  
     protected String databaseClass;
 61  
     protected String defaultSchemaName;
 62  
     protected String changeLogFile;
 63  
     protected String classpath;
 64  
     protected String contexts;
 65  
     protected String driverPropertiesFile;
 66  20
     protected Boolean promptForNonLocalDatabase = null;
 67  
     protected Boolean includeSystemClasspath;
 68  20
     protected String defaultsFile = "liquibase.properties";
 69  
 
 70  
     protected String diffTypes;
 71  
     protected String changeSetAuthor;
 72  
     protected String changeSetContext;
 73  
     protected String dataDir;
 74  
 
 75  
     protected String referenceDriver;
 76  
     protected String referenceUrl;
 77  
     protected String referenceUsername;
 78  
     protected String referencePassword;
 79  
 
 80  
     protected String currentDateTimeFunction;
 81  
 
 82  
     protected String command;
 83  20
     protected Set<String> commandParams = new LinkedHashSet<String>();
 84  
 
 85  
     protected String logLevel;
 86  
     protected String logFile;
 87  
 
 88  20
     protected Map<String, Object> changeLogParameters = new HashMap<String, Object>();
 89  
 
 90  
     public static void main(String args[]) throws CommandLineParsingException, IOException {
 91  
         try {
 92  1
             String shouldRunProperty = System.getProperty(Liquibase.SHOULD_RUN_SYSTEM_PROPERTY);
 93  1
             if (shouldRunProperty != null && !Boolean.valueOf(shouldRunProperty)) {
 94  0
                 System.out.println("Liquibase did not run because '" + Liquibase.SHOULD_RUN_SYSTEM_PROPERTY
 95  
                         + "' system property was set to false");
 96  0
                 return;
 97  
             }
 98  
 
 99  1
             Main main = new Main();
 100  1
             if (args.length == 1 && "--help".equals(args[0])) {
 101  0
                 main.printHelp(System.out);
 102  0
                 return;
 103  1
             } else if (args.length == 1 && "--version".equals(args[0])) {
 104  1
                 System.out.println("Liquibase Version: " + LiquibaseUtil.getBuildVersion()
 105  
                         + StreamUtil.getLineSeparator());
 106  1
                 return;
 107  
             }
 108  
 
 109  
             try {
 110  0
                 main.parseOptions(args);
 111  0
             } catch (CommandLineParsingException e) {
 112  0
                 main.printHelp(Arrays.asList(e.getMessage()), System.out);
 113  0
                 System.exit(-2);
 114  0
             }
 115  
 
 116  0
             File propertiesFile = new File(main.defaultsFile);
 117  0
             File localPropertiesFile = new File(main.defaultsFile.replaceFirst("(\\.[^\\.]+)$", ".local$1"));
 118  
 
 119  0
             if (localPropertiesFile.exists()) {
 120  0
                 main.parsePropertiesFile(new FileInputStream(localPropertiesFile));
 121  
             }
 122  0
             if (propertiesFile.exists()) {
 123  0
                 main.parsePropertiesFile(new FileInputStream(propertiesFile));
 124  
             }
 125  
 
 126  0
             List<String> setupMessages = main.checkSetup();
 127  0
             if (setupMessages.size() > 0) {
 128  0
                 main.printHelp(setupMessages, System.out);
 129  0
                 return;
 130  
             }
 131  
 
 132  
             try {
 133  0
                 main.applyDefaults();
 134  0
                 main.configureClassLoader();
 135  0
                 main.doMigration();
 136  0
             } catch (Throwable e) {
 137  0
                 String message = e.getMessage();
 138  0
                 if (e.getCause() != null) {
 139  0
                     message = e.getCause().getMessage();
 140  
                 }
 141  0
                 if (message == null) {
 142  0
                     message = "Unknown Reason";
 143  
                 }
 144  
 
 145  0
                 if (e.getCause() instanceof ValidationFailedException) {
 146  0
                     ((ValidationFailedException) e.getCause()).printDescriptiveError(System.out);
 147  
                 } else {
 148  0
                     System.out.println("Liquibase Update Failed: " + message);
 149  0
                     LogFactory.getLogger().severe(message, e);
 150  0
                     System.out.println(generateLogLevelWarningMessage());
 151  
                 }
 152  0
                 System.exit(-1);
 153  0
             }
 154  
 
 155  0
             if ("update".equals(main.command)) {
 156  0
                 System.out.println("Liquibase Update Successful");
 157  0
             } else if (main.command.startsWith("rollback") && !main.command.endsWith("SQL")) {
 158  0
                 System.out.println("Liquibase Rollback Successful");
 159  0
             } else if (!main.command.endsWith("SQL")) {
 160  0
                 System.out.println("Liquibase '" + main.command + "' Successful");
 161  
             }
 162  0
         } catch (Throwable e) {
 163  0
             String message = "Unexpected error running Liquibase: " + e.getMessage();
 164  0
             System.out.println(message);
 165  
             try {
 166  0
                 LogFactory.getLogger().severe(message, e);
 167  0
             } catch (Exception e1) {
 168  0
                 e.printStackTrace();
 169  0
             }
 170  0
             System.exit(-3);
 171  0
         }
 172  0
         System.exit(0);
 173  0
     }
 174  
 
 175  
     private static String generateLogLevelWarningMessage() {
 176  0
         Logger logger = LogFactory.getLogger();
 177  0
         if (logger == null || logger.getLogLevel() == null || (logger.getLogLevel().equals(LogLevel.DEBUG))) {
 178  0
             return "";
 179  
         } else {
 180  0
             return "\n\nFor more information, use the --logLevel flag)";
 181  
         }
 182  
     }
 183  
 
 184  
     /**
 185  
      * On windows machines, it splits args on '=' signs. Put it back like it was.
 186  
      */
 187  
     protected String[] fixupArgs(String[] args) {
 188  12
         List<String> fixedArgs = new ArrayList<String>();
 189  
 
 190  57
         for (int i = 0; i < args.length; i++) {
 191  45
             String arg = args[i];
 192  45
             if ((arg.startsWith("--") || arg.startsWith("-D")) && !arg.contains("=")) {
 193  1
                 String nextArg = null;
 194  1
                 if (i + 1 < args.length) {
 195  1
                     nextArg = args[i + 1];
 196  
                 }
 197  1
                 if (nextArg != null && !nextArg.startsWith("--") && !isCommand(nextArg)) {
 198  1
                     arg = arg + "=" + nextArg;
 199  1
                     i++;
 200  
                 }
 201  
             }
 202  45
             fixedArgs.add(arg);
 203  
         }
 204  
 
 205  12
         return fixedArgs.toArray(new String[fixedArgs.size()]);
 206  
     }
 207  
 
 208  
     protected List<String> checkSetup() {
 209  4
         List<String> messages = new ArrayList<String>();
 210  4
         if (command == null) {
 211  2
             messages.add("Command not passed");
 212  2
         } else if (!isCommand(command)) {
 213  1
             messages.add("Unknown command: " + command);
 214  
         } else {
 215  1
             if (url == null) {
 216  0
                 messages.add("--url is required");
 217  
             }
 218  
 
 219  1
             if (isChangeLogRequired(command) && changeLogFile == null) {
 220  0
                 messages.add("--changeLog is required");
 221  
             }
 222  
         }
 223  4
         return messages;
 224  
     }
 225  
 
 226  
     private boolean isChangeLogRequired(String command) {
 227  1
         return command.toLowerCase().startsWith("update") || command.toLowerCase().startsWith("rollback")
 228  
                 || "validate".equals(command);
 229  
     }
 230  
 
 231  
     private boolean isCommand(String arg) {
 232  33
         return "migrate".equals(arg) || "migrateSQL".equalsIgnoreCase(arg) || "update".equalsIgnoreCase(arg)
 233  
                 || "updateSQL".equalsIgnoreCase(arg) || "updateCount".equalsIgnoreCase(arg)
 234  
                 || "updateCountSQL".equalsIgnoreCase(arg) || "rollback".equalsIgnoreCase(arg)
 235  
                 || "rollbackToDate".equalsIgnoreCase(arg) || "rollbackCount".equalsIgnoreCase(arg)
 236  
                 || "rollbackSQL".equalsIgnoreCase(arg) || "rollbackToDateSQL".equalsIgnoreCase(arg)
 237  
                 || "rollbackCountSQL".equalsIgnoreCase(arg) || "futureRollbackSQL".equalsIgnoreCase(arg)
 238  
                 || "updateTestingRollback".equalsIgnoreCase(arg) || "tag".equalsIgnoreCase(arg)
 239  
                 || "listLocks".equalsIgnoreCase(arg) || "dropAll".equalsIgnoreCase(arg)
 240  
                 || "releaseLocks".equalsIgnoreCase(arg) || "status".equalsIgnoreCase(arg)
 241  
                 || "validate".equalsIgnoreCase(arg) || "help".equalsIgnoreCase(arg) || "diff".equalsIgnoreCase(arg)
 242  
                 || "diffChangeLog".equalsIgnoreCase(arg) || "generateChangeLog".equalsIgnoreCase(arg)
 243  
                 || "clearCheckSums".equalsIgnoreCase(arg) || "dbDoc".equalsIgnoreCase(arg)
 244  
                 || "changelogSync".equalsIgnoreCase(arg) || "changelogSyncSQL".equalsIgnoreCase(arg)
 245  
                 || "markNextChangeSetRan".equalsIgnoreCase(arg) || "markNextChangeSetRanSQL".equalsIgnoreCase(arg);
 246  
     }
 247  
 
 248  
     protected void parsePropertiesFile(InputStream propertiesInputStream) throws IOException,
 249  
             CommandLineParsingException {
 250  3
         Properties props = new Properties();
 251  3
         props.load(propertiesInputStream);
 252  
 
 253  3
         for (Map.Entry entry : props.entrySet()) {
 254  
             try {
 255  17
                 if (entry.getKey().equals("promptOnNonLocalDatabase")) {
 256  0
                     continue;
 257  
                 }
 258  17
                 if (((String) entry.getKey()).startsWith("parameter.")) {
 259  0
                     changeLogParameters
 260  
                             .put(((String) entry.getKey()).replaceFirst("^parameter.", ""), entry.getValue());
 261  
                 } else {
 262  17
                     Field field = getClass().getDeclaredField((String) entry.getKey());
 263  16
                     Object currentValue = field.get(this);
 264  
 
 265  16
                     if (currentValue == null) {
 266  14
                         String value = entry.getValue().toString().trim();
 267  14
                         if (field.getType().equals(Boolean.class)) {
 268  2
                             field.set(this, Boolean.valueOf(value));
 269  
                         } else {
 270  12
                             field.set(this, value);
 271  
                         }
 272  
                     }
 273  
                 }
 274  1
             } catch (Exception e) {
 275  1
                 throw new CommandLineParsingException("Unknown parameter: '" + entry.getKey() + "'");
 276  16
             }
 277  
         }
 278  2
     }
 279  
 
 280  
     protected void printHelp(List<String> errorMessages, PrintStream stream) {
 281  0
         stream.println("Errors:");
 282  0
         for (String message : errorMessages) {
 283  0
             stream.println("  " + message);
 284  
         }
 285  0
         stream.println();
 286  0
         printHelp(stream);
 287  0
     }
 288  
 
 289  
     protected void printHelp(PrintStream stream) {
 290  1
         stream.println("Usage: java -jar liquibase.jar [options] [command]");
 291  1
         stream.println("");
 292  1
         stream.println("Standard Commands:");
 293  1
         stream.println(" update                         Updates database to current version");
 294  1
         stream.println(" updateSQL                      Writes SQL to update database to current");
 295  1
         stream.println("                                version to STDOUT");
 296  1
         stream.println(" updateCount <num>              Applies next NUM changes to the database");
 297  1
         stream.println(" updateSQL <num>                Writes SQL to apply next NUM changes");
 298  1
         stream.println("                                to the database");
 299  1
         stream.println(" rollback <tag>                 Rolls back the database to the the state is was");
 300  1
         stream.println("                                when the tag was applied");
 301  1
         stream.println(" rollbackSQL <tag>              Writes SQL to roll back the database to that");
 302  1
         stream.println("                                state it was in when the tag was applied");
 303  1
         stream.println("                                to STDOUT");
 304  1
         stream.println(" rollbackToDate <date/time>     Rolls back the database to the the state is was");
 305  1
         stream.println("                                at the given date/time.");
 306  1
         stream.println("                                Date Format: yyyy-MM-dd HH:mm:ss");
 307  1
         stream.println(" rollbackToDateSQL <date/time>  Writes SQL to roll back the database to that");
 308  1
         stream.println("                                state it was in at the given date/time version");
 309  1
         stream.println("                                to STDOUT");
 310  1
         stream.println(" rollbackCount <value>          Rolls back the last <value> change sets");
 311  1
         stream.println("                                applied to the database");
 312  1
         stream.println(" rollbackCountSQL <value>       Writes SQL to roll back the last");
 313  1
         stream.println("                                <value> change sets to STDOUT");
 314  1
         stream.println("                                applied to the database");
 315  1
         stream.println(" futureRollbackSQL              Writes SQL to roll back the database to the ");
 316  1
         stream.println("                                current state after the changes in the ");
 317  1
         stream.println("                                changeslog have been applied");
 318  1
         stream.println(" updateTestingRollback          Updates database, then rolls back changes before");
 319  1
         stream.println("                                updating again. Useful for testing");
 320  1
         stream.println("                                rollback support");
 321  1
         stream.println(" generateChangeLog              Writes Change Log XML to copy the current state");
 322  1
         stream.println("                                of the database to standard out");
 323  1
         stream.println("");
 324  1
         stream.println("Diff Commands");
 325  1
         stream.println(" diff [diff parameters]          Writes description of differences");
 326  1
         stream.println("                                 to standard out");
 327  1
         stream.println(" diffChangeLog [diff parameters] Writes Change Log XML to update");
 328  1
         stream.println("                                 the database");
 329  1
         stream.println("                                 to the reference database to standard out");
 330  1
         stream.println("");
 331  1
         stream.println("Documentation Commands");
 332  1
         stream.println(" dbDoc <outputDirectory>         Generates Javadoc-like documentation");
 333  1
         stream.println("                                 based on current database and change log");
 334  1
         stream.println("");
 335  1
         stream.println("Maintenance Commands");
 336  1
         stream.println(" tag <tag string>          'Tags' the current database state for future rollback");
 337  1
         stream.println(" status [--verbose]        Outputs count (list if --verbose) of unrun changesets");
 338  1
         stream.println(" validate                  Checks changelog for errors");
 339  1
         stream.println(" clearCheckSums            Removes all saved checksums from database log.");
 340  1
         stream.println("                           Useful for 'MD5Sum Check Failed' errors");
 341  1
         stream.println(" changelogSync             Mark all changes as executed in the database");
 342  1
         stream.println(" changelogSyncSQL          Writes SQL to mark all changes as executed ");
 343  1
         stream.println("                           in the database to STDOUT");
 344  1
         stream.println(" markNextChangeSetRan      Mark the next change changes as executed ");
 345  1
         stream.println("                           in the database");
 346  1
         stream.println(" markNextChangeSetRanSQL   Writes SQL to mark the next change ");
 347  1
         stream.println("                           as executed in the database to STDOUT");
 348  1
         stream.println(" listLocks                 Lists who currently has locks on the");
 349  1
         stream.println("                           database changelog");
 350  1
         stream.println(" releaseLocks              Releases all locks on the database changelog");
 351  1
         stream.println(" dropAll                   Drop all database objects owned by user");
 352  1
         stream.println("");
 353  1
         stream.println("Required Parameters:");
 354  1
         stream.println(" --changeLogFile=<path and filename>        Migration file");
 355  1
         stream.println(" --username=<value>                         Database username");
 356  1
         stream.println(" --password=<value>                         Database password");
 357  1
         stream.println(" --url=<value>                              Database URL");
 358  1
         stream.println("");
 359  1
         stream.println("Optional Parameters:");
 360  1
         stream.println(" --classpath=<value>                        Classpath containing");
 361  1
         stream.println("                                            migration files and JDBC Driver");
 362  1
         stream.println(" --driver=<jdbc.driver.ClassName>           Database driver class name");
 363  1
         stream.println(" --databaseClass=<database.ClassName>       custom liquibase.database.Database");
 364  1
         stream.println("                                            implementation to use");
 365  1
         stream.println(" --defaultSchemaName=<name>                 Default database schema to use");
 366  1
         stream.println(" --contexts=<value>                         ChangeSet contexts to execute");
 367  1
         stream.println(" --defaultsFile=</path/to/file.properties>  File with default option values");
 368  1
         stream.println("                                            (default: ./liquibase.properties)");
 369  1
         stream.println(" --driverPropertiesFile=</path/to/file.properties>  File with custom properties");
 370  1
         stream.println("                                            to be set on the JDBC connection");
 371  1
         stream.println("                                            to be created");
 372  1
         stream.println(" --includeSystemClasspath=<true|false>      Include the system classpath");
 373  1
         stream.println("                                            in the Liquibase classpath");
 374  1
         stream.println("                                            (default: true)");
 375  1
         stream.println(" --promptForNonLocalDatabase=<true|false>   Prompt if non-localhost");
 376  1
         stream.println("                                            databases (default: false)");
 377  1
         stream.println(" --logLevel=<level>                         Execution log level");
 378  1
         stream.println("                                            (debug, info, warning, severe, off");
 379  1
         stream.println(" --logFile=<file>                           Log file");
 380  1
         stream.println(" --currentDateTimeFunction=<value>          Overrides current date time function");
 381  1
         stream.println("                                            used in SQL.");
 382  1
         stream.println("                                            Useful for unsupported databases");
 383  1
         stream.println(" --help                                     Prints this message");
 384  1
         stream.println(" --version                                  Prints this version information");
 385  1
         stream.println("");
 386  1
         stream.println("Required Diff Parameters:");
 387  1
         stream.println(" --referenceUsername=<value>                Reference Database username");
 388  1
         stream.println(" --referencePassword=<value>                Reference Database password");
 389  1
         stream.println(" --referenceUrl=<value>                     Reference Database URL");
 390  1
         stream.println("");
 391  1
         stream.println("Optional Diff Parameters:");
 392  1
         stream.println(" --referenceDriver=<jdbc.driver.ClassName>  Reference Database driver class name");
 393  1
         stream.println(" --dataOutputDirectory=DIR                  Output data as CSV in the given ");
 394  1
         stream.println("                                            directory");
 395  1
         stream.println("");
 396  1
         stream.println("Change Log Properties:");
 397  1
         stream.println(" -D<property.name>=<property.value>         Pass a name/value pair for");
 398  1
         stream.println("                                            substitution in the change log(s)");
 399  1
         stream.println("");
 400  1
         stream.println("Default value for parameters can be stored in a file called");
 401  1
         stream.println("'liquibase.properties' that is read from the current working directory.");
 402  1
         stream.println("");
 403  1
         stream.println("Full documentation is available at");
 404  1
         stream.println("http://www.liquibase.org/manual/command_line");
 405  1
         stream.println("");
 406  1
     }
 407  
 
 408  20
     public Main() {
 409  
         // options = createOptions();
 410  20
     }
 411  
 
 412  
     protected void parseOptions(String[] args) throws CommandLineParsingException {
 413  9
         args = fixupArgs(args);
 414  
 
 415  9
         boolean seenCommand = false;
 416  37
         for (String arg : args) {
 417  30
             if (isCommand(arg)) {
 418  6
                 this.command = arg;
 419  6
                 if (this.command.equalsIgnoreCase("migrate")) {
 420  2
                     this.command = "update";
 421  4
                 } else if (this.command.equalsIgnoreCase("migrateSQL")) {
 422  0
                     this.command = "updateSQL";
 423  
                 }
 424  6
                 seenCommand = true;
 425  24
             } else if (seenCommand) {
 426  1
                 if (arg.startsWith("-D")) {
 427  0
                     String[] splitArg = splitArg(arg);
 428  
 
 429  0
                     String attributeName = splitArg[0].replaceFirst("^-D", "");
 430  0
                     String value = splitArg[1];
 431  
 
 432  0
                     changeLogParameters.put(attributeName, value);
 433  0
                 } else {
 434  1
                     commandParams.add(arg);
 435  
                 }
 436  23
             } else if (arg.startsWith("--")) {
 437  22
                 String[] splitArg = splitArg(arg);
 438  
 
 439  22
                 String attributeName = splitArg[0];
 440  22
                 String value = splitArg[1];
 441  
 
 442  
                 try {
 443  22
                     Field field = getClass().getDeclaredField(attributeName);
 444  21
                     if (field.getType().equals(Boolean.class)) {
 445  5
                         field.set(this, Boolean.valueOf(value));
 446  
                     } else {
 447  16
                         field.set(this, value);
 448  
                     }
 449  1
                 } catch (Exception e) {
 450  1
                     throw new CommandLineParsingException("Unknown parameter: '" + attributeName + "'");
 451  21
                 }
 452  21
             } else {
 453  1
                 throw new CommandLineParsingException("Unexpected value " + arg + ": parameters must start with a '--'");
 454  
             }
 455  
         }
 456  
 
 457  7
     }
 458  
 
 459  
     private String[] splitArg(String arg) throws CommandLineParsingException {
 460  22
         String[] splitArg = arg.split("=", 2);
 461  22
         if (splitArg.length < 2) {
 462  0
             throw new CommandLineParsingException("Could not parse '" + arg + "'");
 463  
         }
 464  
 
 465  22
         splitArg[0] = splitArg[0].replaceFirst("--", "");
 466  22
         return splitArg;
 467  
     }
 468  
 
 469  
     protected void applyDefaults() {
 470  4
         if (this.promptForNonLocalDatabase == null) {
 471  2
             this.promptForNonLocalDatabase = Boolean.FALSE;
 472  
         }
 473  4
         if (this.logLevel == null) {
 474  2
             this.logLevel = "info";
 475  
         }
 476  4
         if (this.includeSystemClasspath == null) {
 477  2
             this.includeSystemClasspath = Boolean.TRUE;
 478  
         }
 479  
 
 480  4
     }
 481  
 
 482  
     protected void configureClassLoader() throws CommandLineParsingException {
 483  2
         final List<URL> urls = new ArrayList<URL>();
 484  2
         if (this.classpath != null) {
 485  
             String[] classpath;
 486  2
             if (isWindows()) {
 487  0
                 classpath = this.classpath.split(";");
 488  
             } else {
 489  2
                 classpath = this.classpath.split(":");
 490  
             }
 491  
 
 492  4
             for (String classpathEntry : classpath) {
 493  3
                 File classPathFile = new File(classpathEntry);
 494  3
                 if (!classPathFile.exists()) {
 495  1
                     throw new CommandLineParsingException(classPathFile.getAbsolutePath() + " does not exist");
 496  
                 }
 497  
                 try {
 498  2
                     if (classpathEntry.endsWith(".war")) {
 499  0
                         addWarFileClasspathEntries(classPathFile, urls);
 500  2
                     } else if (classpathEntry.endsWith(".ear")) {
 501  0
                         JarFile earZip = new JarFile(classPathFile);
 502  
 
 503  0
                         Enumeration<? extends JarEntry> entries = earZip.entries();
 504  0
                         while (entries.hasMoreElements()) {
 505  0
                             JarEntry entry = entries.nextElement();
 506  0
                             if (entry.getName().toLowerCase().endsWith(".jar")) {
 507  0
                                 File jar = extract(earZip, entry);
 508  0
                                 urls.add(new URL("jar:" + jar.toURL() + "!/"));
 509  0
                                 jar.deleteOnExit();
 510  0
                             } else if (entry.getName().toLowerCase().endsWith("war")) {
 511  0
                                 File warFile = extract(earZip, entry);
 512  0
                                 addWarFileClasspathEntries(warFile, urls);
 513  
                             }
 514  0
                         }
 515  
 
 516  0
                     } else {
 517  2
                         urls.add(new File(classpathEntry).toURL());
 518  
                     }
 519  0
                 } catch (Exception e) {
 520  0
                     throw new CommandLineParsingException(e);
 521  2
                 }
 522  
             }
 523  
         }
 524  1
         if (includeSystemClasspath) {
 525  1
             classLoader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
 526  
                 @Override
 527  
                 public URLClassLoader run() {
 528  1
                     return new URLClassLoader(urls.toArray(new URL[urls.size()]), Thread.currentThread()
 529  
                             .getContextClassLoader());
 530  
                 }
 531  
             });
 532  
 
 533  
         } else {
 534  0
             classLoader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
 535  
                 @Override
 536  
                 public URLClassLoader run() {
 537  0
                     return new URLClassLoader(urls.toArray(new URL[urls.size()]));
 538  
                 }
 539  
             });
 540  
         }
 541  
 
 542  1
         ServiceLocator.getInstance().setResourceAccessor(new ClassLoaderResourceAccessor(classLoader));
 543  1
         Thread.currentThread().setContextClassLoader(classLoader);
 544  1
     }
 545  
 
 546  
     private void addWarFileClasspathEntries(File classPathFile, List<URL> urls) throws IOException {
 547  0
         URL url = new URL("jar:" + classPathFile.toURL() + "!/WEB-INF/classes/");
 548  0
         urls.add(url);
 549  0
         JarFile warZip = new JarFile(classPathFile);
 550  0
         Enumeration<? extends JarEntry> entries = warZip.entries();
 551  0
         while (entries.hasMoreElements()) {
 552  0
             JarEntry entry = entries.nextElement();
 553  0
             if (entry.getName().startsWith("WEB-INF/lib") && entry.getName().toLowerCase().endsWith(".jar")) {
 554  0
                 File jar = extract(warZip, entry);
 555  0
                 urls.add(new URL("jar:" + jar.toURL() + "!/"));
 556  0
                 jar.deleteOnExit();
 557  
             }
 558  0
         }
 559  0
     }
 560  
 
 561  
     private File extract(JarFile jar, JarEntry entry) throws IOException {
 562  
         // expand to temp dir and add to list
 563  0
         File tempFile = File.createTempFile("liquibase.tmp", null);
 564  
         // read from jar and write to the tempJar file
 565  0
         BufferedInputStream inStream = null;
 566  
 
 567  0
         BufferedOutputStream outStream = null;
 568  
         try {
 569  0
             inStream = new BufferedInputStream(jar.getInputStream(entry));
 570  0
             outStream = new BufferedOutputStream(new FileOutputStream(tempFile));
 571  
             int status;
 572  0
             while ((status = inStream.read()) != -1) {
 573  0
                 outStream.write(status);
 574  
             }
 575  
         } finally {
 576  0
             if (outStream != null) {
 577  
                 try {
 578  0
                     outStream.close();
 579  0
                 } catch (IOException ioe) {
 580  
                     ;
 581  0
                 }
 582  
             }
 583  0
             if (inStream != null) {
 584  
                 try {
 585  0
                     inStream.close();
 586  0
                 } catch (IOException ioe) {
 587  
                     ;
 588  0
                 }
 589  
             }
 590  
         }
 591  
 
 592  0
         return tempFile;
 593  
     }
 594  
 
 595  
     protected void doMigration() throws Exception {
 596  0
         if ("help".equalsIgnoreCase(command)) {
 597  0
             printHelp(System.out);
 598  0
             return;
 599  
         }
 600  
 
 601  
         try {
 602  0
             if (null != logFile) {
 603  0
                 LogFactory.getLogger().setLogLevel(logLevel, logFile);
 604  
             } else {
 605  0
                 LogFactory.getLogger().setLogLevel(logLevel);
 606  
             }
 607  0
         } catch (IllegalArgumentException e) {
 608  0
             throw new CommandLineParsingException(e.getMessage(), e);
 609  0
         }
 610  
 
 611  0
         FileSystemResourceAccessor fsOpener = new FileSystemResourceAccessor();
 612  0
         CommandLineResourceAccessor clOpener = new CommandLineResourceAccessor(classLoader);
 613  0
         Database database = CommandLineUtils.createDatabaseObject(classLoader, this.url, this.username, this.password,
 614  
                 this.driver, this.defaultSchemaName, this.databaseClass, this.driverPropertiesFile);
 615  
         try {
 616  
 
 617  0
             CompositeResourceAccessor fileOpener = new CompositeResourceAccessor(fsOpener, clOpener);
 618  
 
 619  0
             if ("diff".equalsIgnoreCase(command)) {
 620  0
                 CommandLineUtils.doDiff(createReferenceDatabaseFromCommandParams(commandParams), database);
 621  
                 return;
 622  0
             } else if ("diffChangeLog".equalsIgnoreCase(command)) {
 623  0
                 CommandLineUtils.doDiffToChangeLog(changeLogFile,
 624  
                         createReferenceDatabaseFromCommandParams(commandParams), database);
 625  
                 return;
 626  0
             } else if ("generateChangeLog".equalsIgnoreCase(command)) {
 627  0
                 CommandLineUtils.doGenerateChangeLog(changeLogFile, database, defaultSchemaName,
 628  
                         StringUtils.trimToNull(diffTypes), StringUtils.trimToNull(changeSetAuthor),
 629  
                         StringUtils.trimToNull(changeSetContext), StringUtils.trimToNull(dataDir));
 630  
                 return;
 631  
             }
 632  
 
 633  0
             Liquibase liquibase = new Liquibase(changeLogFile, fileOpener, database);
 634  0
             liquibase.setCurrentDateTimeFunction(currentDateTimeFunction);
 635  0
             for (Map.Entry<String, Object> entry : changeLogParameters.entrySet()) {
 636  0
                 liquibase.setChangeLogParameter(entry.getKey(), entry.getValue());
 637  
             }
 638  
 
 639  0
             if ("listLocks".equalsIgnoreCase(command)) {
 640  0
                 liquibase.reportLocks(System.out);
 641  
                 return;
 642  0
             } else if ("releaseLocks".equalsIgnoreCase(command)) {
 643  0
                 LockService.getInstance(database).forceReleaseLock();
 644  0
                 System.out.println("Successfully released all database change log locks for "
 645  
                         + liquibase.getDatabase().getConnection().getConnectionUserName() + "@"
 646  
                         + liquibase.getDatabase().getConnection().getURL());
 647  
                 return;
 648  0
             } else if ("tag".equalsIgnoreCase(command)) {
 649  0
                 liquibase.tag(commandParams.iterator().next());
 650  0
                 System.out.println("Successfully tagged "
 651  
                         + liquibase.getDatabase().getConnection().getConnectionUserName() + "@"
 652  
                         + liquibase.getDatabase().getConnection().getURL());
 653  
                 return;
 654  0
             } else if ("dropAll".equals(command)) {
 655  0
                 liquibase.dropAll();
 656  0
                 System.out.println("All objects dropped from "
 657  
                         + liquibase.getDatabase().getConnection().getConnectionUserName() + "@"
 658  
                         + liquibase.getDatabase().getConnection().getURL());
 659  
                 return;
 660  0
             } else if ("status".equalsIgnoreCase(command)) {
 661  0
                 boolean runVerbose = false;
 662  
 
 663  0
                 if (commandParams.contains("--verbose")) {
 664  0
                     runVerbose = true;
 665  
                 }
 666  0
                 liquibase.reportStatus(runVerbose, contexts, getOutputWriter());
 667  
                 return;
 668  0
             } else if ("validate".equalsIgnoreCase(command)) {
 669  
                 try {
 670  0
                     liquibase.validate();
 671  0
                 } catch (ValidationFailedException e) {
 672  0
                     e.printDescriptiveError(System.out);
 673  
                     return;
 674  0
                 }
 675  0
                 System.out.println("No validation errors found");
 676  
                 return;
 677  0
             } else if ("clearCheckSums".equalsIgnoreCase(command)) {
 678  0
                 liquibase.clearCheckSums();
 679  
                 return;
 680  0
             } else if ("dbdoc".equalsIgnoreCase(command)) {
 681  0
                 if (commandParams.size() == 0) {
 682  0
                     throw new CommandLineParsingException("dbdoc requires an output directory");
 683  
                 }
 684  0
                 if (changeLogFile == null) {
 685  0
                     throw new CommandLineParsingException("dbdoc requires a changeLog parameter");
 686  
                 }
 687  0
                 liquibase.generateDocumentation(commandParams.iterator().next(), contexts);
 688  
                 return;
 689  
             }
 690  
 
 691  0
             DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
 692  
             try {
 693  0
                 if ("update".equalsIgnoreCase(command)) {
 694  0
                     liquibase.update(contexts);
 695  0
                 } else if ("changelogSync".equalsIgnoreCase(command)) {
 696  0
                     liquibase.changeLogSync(contexts);
 697  0
                 } else if ("changelogSyncSQL".equalsIgnoreCase(command)) {
 698  0
                     liquibase.changeLogSync(contexts, getOutputWriter());
 699  0
                 } else if ("markNextChangeSetRan".equalsIgnoreCase(command)) {
 700  0
                     liquibase.markNextChangeSetRan(contexts);
 701  0
                 } else if ("markNextChangeSetRanSQL".equalsIgnoreCase(command)) {
 702  0
                     liquibase.markNextChangeSetRan(contexts, getOutputWriter());
 703  0
                 } else if ("updateCount".equalsIgnoreCase(command)) {
 704  0
                     liquibase.update(Integer.parseInt(commandParams.iterator().next()), contexts);
 705  0
                 } else if ("updateCountSQL".equalsIgnoreCase(command)) {
 706  0
                     liquibase.update(Integer.parseInt(commandParams.iterator().next()), contexts, getOutputWriter());
 707  0
                 } else if ("updateSQL".equalsIgnoreCase(command)) {
 708  0
                     liquibase.update(contexts, getOutputWriter());
 709  0
                 } else if ("rollback".equalsIgnoreCase(command)) {
 710  0
                     if (commandParams == null || commandParams.size() == 0) {
 711  0
                         throw new CommandLineParsingException("rollback requires a rollback tag");
 712  
                     }
 713  0
                     liquibase.rollback(commandParams.iterator().next(), contexts);
 714  0
                 } else if ("rollbackToDate".equalsIgnoreCase(command)) {
 715  0
                     if (commandParams == null || commandParams.size() == 0) {
 716  0
                         throw new CommandLineParsingException("rollback requires a rollback date");
 717  
                     }
 718  0
                     liquibase.rollback(dateFormat.parse(commandParams.iterator().next()), contexts);
 719  0
                 } else if ("rollbackCount".equalsIgnoreCase(command)) {
 720  0
                     liquibase.rollback(Integer.parseInt(commandParams.iterator().next()), contexts);
 721  
 
 722  0
                 } else if ("rollbackSQL".equalsIgnoreCase(command)) {
 723  0
                     if (commandParams == null || commandParams.size() == 0) {
 724  0
                         throw new CommandLineParsingException("rollbackSQL requires a rollback tag");
 725  
                     }
 726  0
                     liquibase.rollback(commandParams.iterator().next(), contexts, getOutputWriter());
 727  0
                 } else if ("rollbackToDateSQL".equalsIgnoreCase(command)) {
 728  0
                     if (commandParams == null || commandParams.size() == 0) {
 729  0
                         throw new CommandLineParsingException("rollbackToDateSQL requires a rollback date");
 730  
                     }
 731  0
                     liquibase.rollback(dateFormat.parse(commandParams.iterator().next()), contexts, getOutputWriter());
 732  0
                 } else if ("rollbackCountSQL".equalsIgnoreCase(command)) {
 733  0
                     if (commandParams == null || commandParams.size() == 0) {
 734  0
                         throw new CommandLineParsingException("rollbackCountSQL requires a rollback tag");
 735  
                     }
 736  
 
 737  0
                     liquibase.rollback(Integer.parseInt(commandParams.iterator().next()), contexts, getOutputWriter());
 738  0
                 } else if ("futureRollbackSQL".equalsIgnoreCase(command)) {
 739  0
                     liquibase.futureRollbackSQL(contexts, getOutputWriter());
 740  0
                 } else if ("updateTestingRollback".equalsIgnoreCase(command)) {
 741  0
                     liquibase.updateTestingRollback(contexts);
 742  
                 } else {
 743  0
                     throw new CommandLineParsingException("Unknown command: " + command);
 744  
                 }
 745  0
             } catch (ParseException e) {
 746  0
                 throw new CommandLineParsingException("Unexpected date/time format.  Use 'yyyy-MM-dd'T'HH:mm:ss'");
 747  0
             }
 748  
         } finally {
 749  0
             try {
 750  0
                 database.rollback();
 751  0
                 database.close();
 752  0
             } catch (DatabaseException e) {
 753  0
                 LogFactory.getLogger().warning("problem closing connection", e);
 754  0
             }
 755  0
         }
 756  0
     }
 757  
 
 758  
     private String getCommandParam(String paramName) throws CommandLineParsingException {
 759  0
         for (String param : commandParams) {
 760  0
             String[] splitArg = splitArg(param);
 761  
 
 762  0
             String attributeName = splitArg[0];
 763  0
             String value = splitArg[1];
 764  0
             if (attributeName.equalsIgnoreCase(paramName)) {
 765  0
                 return value;
 766  
             }
 767  0
         }
 768  
 
 769  0
         return null;
 770  
     }
 771  
 
 772  
     private Database createReferenceDatabaseFromCommandParams(Set<String> commandParams)
 773  
             throws CommandLineParsingException, DatabaseException {
 774  0
         String driver = referenceDriver;
 775  0
         String url = referenceUrl;
 776  0
         String username = referenceUsername;
 777  0
         String password = referencePassword;
 778  0
         String defaultSchemaName = this.defaultSchemaName;
 779  
 
 780  0
         for (String param : commandParams) {
 781  0
             String[] splitArg = splitArg(param);
 782  
 
 783  0
             String attributeName = splitArg[0];
 784  0
             String value = splitArg[1];
 785  0
             if ("referenceDriver".equalsIgnoreCase(attributeName)) {
 786  0
                 driver = value;
 787  0
             } else if ("referenceUrl".equalsIgnoreCase(attributeName)) {
 788  0
                 url = value;
 789  0
             } else if ("referenceUsername".equalsIgnoreCase(attributeName)) {
 790  0
                 username = value;
 791  0
             } else if ("referencePassword".equalsIgnoreCase(attributeName)) {
 792  0
                 password = value;
 793  0
             } else if ("referenceDefaultSchemaName".equalsIgnoreCase(attributeName)) {
 794  0
                 defaultSchemaName = value;
 795  0
             } else if ("dataOutputDirectory".equalsIgnoreCase(attributeName)) {
 796  0
                 dataDir = value;
 797  
             }
 798  0
         }
 799  
 
 800  
         // if (driver == null) {
 801  
         // driver = DatabaseFactory.getWriteExecutor().findDefaultDriver(url);
 802  
         // }
 803  
 
 804  0
         if (url == null) {
 805  0
             throw new CommandLineParsingException("referenceUrl parameter missing");
 806  
         }
 807  
 
 808  0
         return CommandLineUtils.createDatabaseObject(classLoader, url, username, password, driver, defaultSchemaName,
 809  
                 null, null);
 810  
         // Driver driverObject;
 811  
         // try {
 812  
         // driverObject = (Driver) Class.forName(driver, true, classLoader).newInstance();
 813  
         // } catch (Exception e) {
 814  
         // throw new RuntimeException("Cannot find database driver: " + e.getMessage());
 815  
         // }
 816  
         //
 817  
         // Properties info = new Properties();
 818  
         // info.put("user", username);
 819  
         // info.put("password", password);
 820  
         //
 821  
         // Connection connection;
 822  
         // try {
 823  
         // connection = driverObject.connect(url, info);
 824  
         // } catch (SQLException e) {
 825  
         // throw new DatabaseException("Connection could not be created to " + url + ": " + e.getMessage(), e);
 826  
         // }
 827  
         // if (connection == null) {
 828  
         // throw new DatabaseException("Connection could not be created to " + url + " with driver " +
 829  
         // driver.getClass().getName() + ".  Possibly the wrong driver for the given database URL");
 830  
         // }
 831  
         //
 832  
         // Database database = DatabaseFactory.getWriteExecutor().findCorrectDatabaseImplementation(connection);
 833  
         // database.setDefaultSchemaName(defaultSchemaName);
 834  
         //
 835  
         // return database;
 836  
     }
 837  
 
 838  
     private Writer getOutputWriter() {
 839  0
         return new OutputStreamWriter(System.out);
 840  
     }
 841  
 
 842  
     public boolean isWindows() {
 843  4
         return System.getProperty("os.name").startsWith("Windows ");
 844  
     }
 845  
 }