You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ibatis.apache.org by cb...@apache.org on 2008/08/22 22:00:00 UTC

svn commit: r688187 - in /ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis: adhoc/AdHocExecutor.java migration/CommandLine.java migration/Migrator.java

Author: cbegin
Date: Fri Aug 22 13:00:00 2008
New Revision: 688187

URL: http://svn.apache.org/viewvc?rev=688187&view=rev
Log:
Implemented proper undo functionality

Modified:
    ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/adhoc/AdHocExecutor.java
    ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/CommandLine.java
    ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/Migrator.java

Modified: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/adhoc/AdHocExecutor.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/adhoc/AdHocExecutor.java?rev=688187&r1=688186&r2=688187&view=diff
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/adhoc/AdHocExecutor.java (original)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/adhoc/AdHocExecutor.java Fri Aug 22 13:00:00 2008
@@ -168,6 +168,14 @@
     }
   }
 
+  public void closeConnection() {
+    try {
+      connection.close();
+    } catch (SQLException e) {
+      //ignore
+    }
+  }
+
   private void setParameters(PreparedStatement ps, Object... args) throws SQLException {
     for (int i = 0, n = args.length; i < n; i++) {
       if (args[i] == null) {

Modified: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/CommandLine.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/CommandLine.java?rev=688187&r1=688186&r2=688187&view=diff
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/CommandLine.java (original)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/CommandLine.java Fri Aug 22 13:00:00 2008
@@ -13,14 +13,15 @@
   private static final String PATH_PREFIX = "--path=";
   private static final String ENV_PREFIX = "--env=";
   private static final String FORCE = "--force";
-  private static final String CHANGELOG = "changelog";
+  private static final String HELP = "--help";
+  private static final String STATUS = "status";
   private static final String INIT = "init";
   private static final String NEW = "new";
   private static final String RUN = "run";
   private static final String VERSION = "version";
   private static final String UNDO = "undo";
   private static final Set<String> KNOWN_COMMANDS = Collections.unmodifiableSet(
-      new HashSet<String>(Arrays.asList(INIT, NEW, RUN, VERSION, UNDO, CHANGELOG)));
+      new HashSet<String>(Arrays.asList(INIT, NEW, RUN, VERSION, UNDO, STATUS)));
 
   private String repository;
   private String environment;
@@ -85,8 +86,8 @@
       migrator.initialize();
     } else if (NEW.equals(command)) {
       migrator.newMigration(params);
-    } else if (CHANGELOG.equals(command)) {
-      migrator.printChangelog();
+    } else if (STATUS.equals(command)) {
+      migrator.printStatus();
     } else if (RUN.equals(command)) {
       migrator.runPendingMigrations();
     } else if (VERSION.equals(command)) {
@@ -126,9 +127,9 @@
     out.println("  init               Creates (if necessary) and initializes a migration path.");
     out.println("  new <description>  Creates a new migration with the provided description.");
     out.println("  run                Run all unapplied migrations.");
-    out.println("  changelog          Prints the changelog from the database.");
     out.println("  version <version>  Migrates the database up or down to the specified version.");
     out.println("  undo               Undoes the last migration applied to the database.");
+    out.println("  status             Prints the changelog from the database if the changelog table exists.");
     out.println();
     out.flush();
   }
@@ -139,9 +140,9 @@
         repository = arg.split("=")[1];
       } else if (arg.startsWith(ENV_PREFIX) && arg.length() > ENV_PREFIX.length()) {
         environment = arg.split("=")[1];
-      } else if (arg.startsWith("--force")) {
+      } else if (arg.startsWith(FORCE)) {
         force = true;
-      } else if (arg.startsWith("--help")) {
+      } else if (arg.startsWith(HELP)) {
         help = true;
       } else if (command == null) {
         command = arg;

Modified: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/Migrator.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/Migrator.java?rev=688187&r1=688186&r2=688187&view=diff
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/Migrator.java (original)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/Migrator.java Fri Aug 22 13:00:00 2008
@@ -16,6 +16,7 @@
   private File repository;
   private String environment;
   private boolean force;
+  private PrintStream out = System.out;
 
   public Migrator(String repository, String environment, boolean force) {
     this.repository = new File(repository);
@@ -28,13 +29,13 @@
   }
 
   public void initialize() {
-    createIfNecessary(repository);
-    ensureEmpty(repository);
-    System.out.println("Initializing: " + repository);
-    copyResourceTo("org/apache/ibatis/migration/template_environment.properties", environmentFile(environment));
-    copyResourceTo("org/apache/ibatis/migration/template_changelog.sql", repositoryFile(getTimestamp() + "_create_changelog.sql"));
-    copyResourceTo("org/apache/ibatis/migration/template_migration.sql", repositoryFile(getTimestamp() + "_first_migration.sql"));
-    System.out.println("Done!");
+    createDirectoryIfNecessary(repository);
+    ensureDirectoryIsEmpty(repository);
+    out.println("Initializing: " + repository);
+    copyResourceTo("org/apache/ibatis/migration/template_environment.properties", environmentFile());
+    copyResourceTo("org/apache/ibatis/migration/template_changelog.sql", repositoryFile(getTimestampAsString() + "_create_changelog.sql"));
+    copyResourceTo("org/apache/ibatis/migration/template_migration.sql", repositoryFile(getTimestampAsString() + "_first_migration.sql"));
+    out.println("Done!");
   }
 
   public void newMigration(String description) {
@@ -43,10 +44,10 @@
     }
     Map<String, String> variables = new HashMap<String, String>();
     variables.put("description", description);
-    ensureEnvironment(environment);
-    String filename = getTimestamp() + "_" + description.replace(' ', '_') + ".sql";
+    existingEnvironmentFile();
+    String filename = getTimestampAsString() + "_" + description.replace(' ', '_') + ".sql";
     copyResourceTo("org/apache/ibatis/migration/template_migration.sql", repositoryFile(filename), variables);
-    System.out.println("Done!");
+    out.println("Done!");
   }
 
   public void runPendingMigrations() {
@@ -55,7 +56,7 @@
       Arrays.sort(filenames);
       for (String filename : filenames) {
         if (filename.endsWith(".sql")) {
-          System.out.println(horizontalLine("Applying: " + filename, 80));
+          out.println(horizontalLine("Applying: " + filename, 80));
           ScriptRunner runner = getScriptRunner();
           runner.runScript(new MigrationReader(new FileReader(repositoryFile(filename)), false));
           Change change = parseChangeFromFilename(filename);
@@ -68,33 +69,28 @@
   }
 
   public void migrateToVersion(BigInteger version) {
-    getChangelog();
-  }
-
-  public void printChangelog() {
-    List<Change> changelog = getChangelog();
-    System.out.println("ID             TIMESTAMP             DESCRIPTION");
-    System.out.println(horizontalLine("",60));
-    for (Change change : changelog) {
-      System.out.println(change);
-    }
+    out.println("not implemented");
   }
 
   public void undoLastMigration() {
     try {
       String[] filenames = repository.list();
-      Arrays.sort(filenames, new Comparator() {
-        public int compare(Object o1, Object o2) {
-          return ((Comparable) o2).compareTo(o1);
-        }
-      });
+      reverse(filenames);
+      Change lastChange = getLastChange();
       for (String filename : filenames) {
         if (filename.endsWith(".sql")) {
-          System.out.println(horizontalLine("Undoing: " + filename, 80));
-          ScriptRunner runner = getScriptRunner();
-          runner.runScript(new MigrationReader(new FileReader(repositoryFile(filename)), true));
           Change change = parseChangeFromFilename(filename);
-          deleteChange(change);
+          if (change.getId().equals(lastChange.getId())) {
+            out.println(horizontalLine("Undoing: " + filename, 80));
+            ScriptRunner runner = getScriptRunner();
+            runner.runScript(new MigrationReader(new FileReader(repositoryFile(filename)), true));
+            if (changelogExists()) {
+              deleteChange(change);
+            } else {
+              out.println("Changelog doesn't exist. No further migrations will be undone (normal for the last migration).");
+            }
+            break;            
+          }
         }
       }
     } catch (Exception e) {
@@ -102,66 +98,104 @@
     }
   }
 
+  public void printStatus() {
+    if (changelogExists()) {
+      List<Change> changelog = getChangelog();
+      out.println("ID             TIMESTAMP             DESCRIPTION");
+      out.println(horizontalLine("", 60));
+      for (Change change : changelog) {
+        out.println(change);
+      }
+    } else {
+      out.println("Changelog does not exist.");
+    }
+  }
+
+  private void reverse(Comparable[] comparable) {
+    Arrays.sort(comparable, new Comparator() {
+      public int compare(Object o1, Object o2) {
+        return ((Comparable) o2).compareTo(o1);
+      }
+    });
+  }
+
   private void insertChangelog(Change change) {
+    AdHocExecutor executor = getAdHocExecutor();
     try {
-      AdHocExecutor executor = getAdHocExecutor();
       executor.insert("insert into CHANGELOG (ID, DESCRIPTION) values (?,?)", change.getId(), change.getDescription());
     } catch (SQLException e) {
       throw new MigrationException("Error querying last applied migration.  Cause: " + e, e);
+    } finally {
+      executor.closeConnection();
     }
   }
 
   private void deleteChange(Change change) {
+    AdHocExecutor executor = getAdHocExecutor();
     try {
-      AdHocExecutor executor = getAdHocExecutor();
       executor.delete("delete from CHANGELOG where id = ?", change.getId());
     } catch (SQLException e) {
       throw new MigrationException("Error querying last applied migration.  Cause: " + e, e);
+    } finally {
+      executor.closeConnection();
     }
   }
 
   private Change parseChangeFromFilename(String filename) {
-    Change change = new Change();
-    String[] parts = filename.split("\\.")[0].split("_");
-    change.setId(new BigDecimal(parts[0]));
-    StringBuilder builder = new StringBuilder();
-    for (int i=1; i < parts.length; i++) {
-      if (i > 1) builder.append(" ");
-      builder.append(parts[i]);
+    try {
+      Change change = new Change();
+      String[] parts = filename.split("\\.")[0].split("_");
+      change.setId(new BigDecimal(parts[0]));
+      StringBuilder builder = new StringBuilder();
+      for (int i = 1; i < parts.length; i++) {
+        if (i > 1) builder.append(" ");
+        builder.append(parts[i]);
+      }
+      change.setDescription(builder.toString());
+      return change;
+    } catch (Exception e) {
+      throw new MigrationException("Error parsing change from file.  Cause: " + e, e);
     }
-    change.setDescription(builder.toString());
-    return change;
   }
 
-  private List<Change>getChangelog() {
+  private List<Change> getChangelog() {
+    AdHocExecutor executor = getAdHocExecutor();
     try {
-      AdHocExecutor executor = getAdHocExecutor();
-      List<Map<String,Object>> changelog = executor.selectAll("select ID, DESCRIPTION from CHANGELOG order by id");
+      List<Map<String, Object>> changelog = executor.selectAll("select ID, DESCRIPTION from CHANGELOG order by id");
       List<Change> changes = new ArrayList<Change>();
-      for(Map<String,Object> change : changelog) {
-        changes.add(new Change(new BigDecimal(change.get("ID").toString()),change.get("DESCRIPTION").toString()));
+      for (Map<String, Object> change : changelog) {
+        changes.add(new Change(new BigDecimal(change.get("ID").toString()), change.get("DESCRIPTION").toString()));
       }
       return changes;
     } catch (SQLException e) {
       throw new MigrationException("Error querying last applied migration.  Cause: " + e, e);
+    } finally {
+      executor.closeConnection();
     }
   }
 
-  private AdHocExecutor getAdHocExecutor() {
-    Properties props = getEnvironmentProperties();
-    String driver = props.getProperty("driver");
-    String url = props.getProperty("url");
-    String username = props.getProperty("username");
-    String password = props.getProperty("password");
-    AdHocExecutor executor = new AdHocExecutor(driver, url, username, password, false);
-    return executor;
+  private Change getLastChange() {
+    List<Change> changelog = getChangelog();
+    return changelog.get(changelog.size() - 1);
+  }
+
+  private boolean changelogExists() {
+    AdHocExecutor executor = getAdHocExecutor();
+    try {
+      executor.selectAll("select ID, DESCRIPTION from CHANGELOG");
+      return true;
+    } catch (SQLException e) {
+      return false;
+    } finally {
+      executor.closeConnection();
+    }
   }
 
   private String horizontalLine(String caption, int length) {
     StringBuilder builder = new StringBuilder();
     builder.append("==========");
     if (caption.length() > 0) {
-      caption = " "+caption+" ";
+      caption = " " + caption + " ";
       builder.append(caption);
     }
     for (int i = 0; i < length - caption.length(); i++) {
@@ -170,7 +204,7 @@
     return builder.toString();
   }
 
-  private String getTimestamp() {
+  private String getTimestampAsString() {
     try {
       // Ensure that two subsequent calls are less likely to return the same value.
       Thread.sleep(1000);
@@ -189,7 +223,7 @@
   }
 
   private void copyResourceTo(String resource, File toFile, Map<String, String> variables) {
-    System.out.println("Creating: " + toFile.getName());
+    out.println("Creating: " + toFile.getName());
     try {
       LineNumberReader reader = new LineNumberReader(Resources.getResourceAsReader(this.getClass().getClassLoader(), resource));
       try {
@@ -211,7 +245,7 @@
     }
   }
 
-  private void ensureEmpty(File path) {
+  private void ensureDirectoryIsEmpty(File path) {
     String[] list = path.list();
     if (list.length != 0) {
       for (String entry : list) {
@@ -222,21 +256,10 @@
     }
   }
 
-  private void ensureEnvironment(String environment) {
-    File envFile = environmentFile(environment);
-    if (!envFile.exists()) {
-      throw new MigrationException("Environment file missing: " + envFile.getAbsolutePath());
-    }
-  }
-
-  private File environmentFile(String environment) {
-    return repositoryFile(environment + ".properties");
-  }
-
-  private void createIfNecessary(File path) {
+  private void createDirectoryIfNecessary(File path) {
     if (!path.exists()) {
       File parent = new File(path.getParent());
-      createIfNecessary(parent);
+      createDirectoryIfNecessary(parent);
       if (!path.mkdir()) {
         throw new MigrationException("Could not create directory path for an unknown reason. Make sure you have access to the directory.");
       }
@@ -268,6 +291,16 @@
     return newString;
   }
 
+  private AdHocExecutor getAdHocExecutor() {
+    Properties props = getEnvironmentProperties();
+    String driver = props.getProperty("driver");
+    String url = props.getProperty("url");
+    String username = props.getProperty("username");
+    String password = props.getProperty("password");
+    AdHocExecutor executor = new AdHocExecutor(driver, url, username, password, false);
+    return executor;
+  }
+
   private ScriptRunner getScriptRunner() {
     try {
       Properties props = getEnvironmentProperties();
@@ -281,13 +314,22 @@
     }
   }
 
+  private File environmentFile() {
+    return repositoryFile(environment + ".properties");
+  }
+
+  private File existingEnvironmentFile() {
+    File envFile = environmentFile();
+    if (!envFile.exists()) {
+      throw new MigrationException("Environment file missing: " + envFile.getAbsolutePath());
+    }
+    return envFile;
+  }
+
   private Properties getEnvironmentProperties() {
     try {
+      File file = existingEnvironmentFile();
       Properties props = new Properties();
-      File file = environmentFile(environment);
-      if (!file.exists()) {
-        throw new MigrationException("Could not find environment properties file: " + file.getAbsolutePath());
-      }
       props.load(new FileInputStream(file));
       return props;
     } catch (IOException e) {