| 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 |  |   | 
  | 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 |  |   | 
  | 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 |  |           | 
  | 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 |  |           | 
  | 563 | 0 |          File tempFile = File.createTempFile("liquibase.tmp", null); | 
  | 564 |  |           | 
  | 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 |  |           | 
  | 801 |  |           | 
  | 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 |  |           | 
  | 811 |  |           | 
  | 812 |  |           | 
  | 813 |  |           | 
  | 814 |  |           | 
  | 815 |  |           | 
  | 816 |  |           | 
  | 817 |  |           | 
  | 818 |  |           | 
  | 819 |  |           | 
  | 820 |  |           | 
  | 821 |  |           | 
  | 822 |  |           | 
  | 823 |  |           | 
  | 824 |  |           | 
  | 825 |  |           | 
  | 826 |  |           | 
  | 827 |  |           | 
  | 828 |  |           | 
  | 829 |  |           | 
  | 830 |  |           | 
  | 831 |  |           | 
  | 832 |  |           | 
  | 833 |  |           | 
  | 834 |  |           | 
  | 835 |  |           | 
  | 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 |  |  } |