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 23:48:31 UTC
svn commit: r688202 - in
/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration:
./ commands/
Author: cbegin
Date: Fri Aug 22 14:48:30 2008
New Revision: 688202
URL: http://svn.apache.org/viewvc?rev=688202&view=rev
Log:
Split migrator into command pattern.
Added:
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/BaseCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/Command.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/InitializeCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/NewCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/RunCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/StatusCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/UndoCommand.java
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/VersionCommand.java
Modified:
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
ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/ScriptRunner.java
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=688202&r1=688201&r2=688202&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 14:48:30 2008
@@ -1,143 +1,90 @@
package org.apache.ibatis.migration;
+import org.apache.ibatis.migration.commands.*;
+
+import java.io.File;
+import java.io.PrintStream;
import java.util.Arrays;
-import java.util.Set;
import java.util.Collections;
import java.util.HashSet;
-import java.io.File;
-import java.io.PrintStream;
-import java.math.BigInteger;
+import java.util.Set;
public class CommandLine {
+ protected static final PrintStream out = System.out;
+
private static final String PATH_PREFIX = "--path=";
private static final String ENV_PREFIX = "--env=";
private static final String FORCE = "--force";
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 String STATUS = "status";
+
private static final Set<String> KNOWN_COMMANDS = Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList(INIT, NEW, RUN, VERSION, UNDO, STATUS)));
- private String repository;
+ private File repository;
private String environment;
+ private boolean force;
+
private String command;
private String params;
+
private String parseError;
private boolean help;
- private boolean force;
public CommandLine(String[] args) {
parse(args);
validate();
}
- public String getRepository() {
- return repository;
- }
-
- public String getEnvironment() {
- return environment;
- }
-
- public String getCommand() {
- return command;
- }
-
- public String getParams() {
- return params;
- }
-
- public boolean isHelp() {
- return help;
- }
-
- public boolean isValid() {
- return parseError == null;
- }
-
- public String toString() {
- return repository + " " + environment + " " + command + " " + (params == null ? "" : params);
- }
-
public void execute() {
- if (isHelp()) {
- printUsage();
- } else if (!isValid()) {
- printError();
- printUsage();
- } else {
- try {
- runCommand();
- } catch (MigrationException e) {
- System.err.println(e.getMessage());
+ try {
+ if (help) {
+ printUsage();
+ } else if (parseError != null) {
+ printError();
printUsage();
+ } else {
+ try {
+ runCommand();
+ } catch (MigrationException e) {
+ out.println("ERROR: " + e.getMessage());
+ printUsage();
+ }
}
+ } finally {
+ out.flush();
}
}
private void runCommand() {
- Migrator migrator = new Migrator(repository, environment, force);
if (INIT.equals(command)) {
- migrator.initialize();
+ new InitializeCommand(repository,environment,force).execute(params);
} else if (NEW.equals(command)) {
- migrator.newMigration(params);
+ new NewCommand(repository,environment,force).execute(params);
} else if (STATUS.equals(command)) {
- migrator.printStatus();
+ new StatusCommand(repository,environment,force).execute(params);
} else if (RUN.equals(command)) {
- migrator.runPendingMigrations();
+ new RunCommand(repository,environment,force).execute(params);
} else if (VERSION.equals(command)) {
- BigInteger version = null;
- try {
- version = new BigInteger(params);
- } catch(Exception e) {
- System.err.println("Invalid version number specified: " + params);
- printUsage();
- }
- migrator.migrateToVersion(version);
+ new VersionCommand(repository,environment,force).execute(params);
} else if (UNDO.equals(command)) {
- migrator.undoLastMigration();
+ new UndoCommand(repository,environment,force).execute(params);
} else {
throw new RuntimeException("Attempt to execute unkown command.");
}
}
- public void printError() {
- PrintStream out = System.err;
- out.println(parseError);
- out.flush();
- }
-
- public void printUsage() {
- PrintStream out = System.out;
- out.println();
- out.println("Usage: migrate command [parameter] [--path=<directory>] [--env=<environment>]");
- out.println();
- out.println("--path=<directory> Path to repository. Default current working directory.");
- out.println("--env=<environment> Environment to configure. Default environment is 'development'.");
- out.println("--env=<environment> Environment to configure. Default environment is 'development'.");
- out.println("--force Forces script to continue even if SQL errors are encountered.");
- out.println("--help Displays this usage message.");
- out.println();
- out.println("Commands:");
- 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(" 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();
- }
-
private void parse(String[] args) {
for (String arg : args) {
if (arg.startsWith(PATH_PREFIX) && arg.length() > PATH_PREFIX.length()) {
- repository = arg.split("=")[1];
+ repository = new File(arg.split("=")[1]);
} else if (arg.startsWith(ENV_PREFIX) && arg.length() > ENV_PREFIX.length()) {
environment = arg.split("=")[1];
} else if (arg.startsWith(FORCE)) {
@@ -157,16 +104,15 @@
private void validate() {
if (repository == null) {
- repository = "./";
+ repository = new File("./");
}
if (environment == null) {
environment = "development";
}
- File f = new File(repository);
- if (f.exists() && !f.isDirectory()) {
- parseError = ("Migrations path must be a directory: " + f.getAbsolutePath());
+ if (repository.exists() && !repository.isDirectory()) {
+ parseError = ("Migrations path must be a directory: " + repository.getAbsolutePath());
} else {
- repository = f.getAbsolutePath();
+ repository = new File(repository.getAbsolutePath());
if (command == null) {
parseError = "No command specified.";
} else {
@@ -177,4 +123,29 @@
}
}
+ private void printError() {
+ out.println(parseError);
+ out.flush();
+ }
+
+ private void printUsage() {
+ out.println();
+ out.println("Usage: migrate command [parameter] [--path=<directory>] [--env=<environment>]");
+ out.println();
+ out.println("--path=<directory> Path to repository. Default current working directory.");
+ out.println("--env=<environment> Environment to configure. Default environment is 'development'.");
+ out.println("--force Forces script to continue even if SQL errors are encountered.");
+ out.println("--help Displays this usage message.");
+ out.println();
+ out.println("Commands:");
+ 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(" 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();
+ }
+
}
\ No newline at end of file
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=688202&r1=688201&r2=688202&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 14:48:30 2008
@@ -1,340 +1,9 @@
package org.apache.ibatis.migration;
-import org.apache.ibatis.io.Resources;
-import org.apache.ibatis.adhoc.AdHocExecutor;
-
-import java.math.BigInteger;
-import java.math.BigDecimal;
-import java.io.*;
-import java.text.SimpleDateFormat;
-import java.sql.Date;
-import java.sql.SQLException;
-import java.util.*;
-
public class Migrator {
- 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);
- this.environment = environment;
- this.force = force;
- }
-
public static void main(String[] args) throws Exception {
new CommandLine(args).execute();
}
- public void initialize() {
- 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) {
- if (description == null) {
- throw new MigrationException("No description specified for new migration.");
- }
- Map<String, String> variables = new HashMap<String, String>();
- variables.put("description", description);
- existingEnvironmentFile();
- String filename = getTimestampAsString() + "_" + description.replace(' ', '_') + ".sql";
- copyResourceTo("org/apache/ibatis/migration/template_migration.sql", repositoryFile(filename), variables);
- out.println("Done!");
- }
-
- public void runPendingMigrations() {
- try {
- String[] filenames = repository.list();
- Arrays.sort(filenames);
- for (String filename : filenames) {
- if (filename.endsWith(".sql")) {
- out.println(horizontalLine("Applying: " + filename, 80));
- ScriptRunner runner = getScriptRunner();
- runner.runScript(new MigrationReader(new FileReader(repositoryFile(filename)), false));
- Change change = parseChangeFromFilename(filename);
- insertChangelog(change);
- }
- }
- } catch (Exception e) {
- throw new RuntimeException("<Description>. Cause: " + e, e);
- }
- }
-
- public void migrateToVersion(BigInteger version) {
- out.println("not implemented");
- }
-
- public void undoLastMigration() {
- try {
- String[] filenames = repository.list();
- reverse(filenames);
- Change lastChange = getLastChange();
- for (String filename : filenames) {
- if (filename.endsWith(".sql")) {
- Change change = parseChangeFromFilename(filename);
- 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) {
- throw new RuntimeException("<Description>. Cause: " + e, e);
- }
- }
-
- 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 {
- 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 {
- 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) {
- 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);
- }
- }
-
- private List<Change> getChangelog() {
- AdHocExecutor executor = getAdHocExecutor();
- try {
- 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()));
- }
- return changes;
- } catch (SQLException e) {
- throw new MigrationException("Error querying last applied migration. Cause: " + e, e);
- } finally {
- executor.closeConnection();
- }
- }
-
- 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 + " ";
- builder.append(caption);
- }
- for (int i = 0; i < length - caption.length(); i++) {
- builder.append("=");
- }
- return builder.toString();
- }
-
- private String getTimestampAsString() {
- try {
- // Ensure that two subsequent calls are less likely to return the same value.
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- //ignore
- }
- return new SimpleDateFormat("yyyyMMddHHmmss").format(new Date(System.currentTimeMillis()));
- }
-
- private File repositoryFile(String fileName) {
- return new File(repository.getAbsolutePath() + File.separator + fileName);
- }
-
- private void copyResourceTo(String resource, File toFile) {
- copyResourceTo(resource, toFile, null);
- }
-
- private void copyResourceTo(String resource, File toFile, Map<String, String> variables) {
- out.println("Creating: " + toFile.getName());
- try {
- LineNumberReader reader = new LineNumberReader(Resources.getResourceAsReader(this.getClass().getClassLoader(), resource));
- try {
- PrintWriter writer = new PrintWriter(new FileWriter(toFile));
- try {
- String line;
- while ((line = reader.readLine()) != null) {
- line = parsePlaceholders(line, variables);
- writer.println(line);
- }
- } finally {
- writer.close();
- }
- } finally {
- reader.close();
- }
- } catch (IOException e) {
- throw new MigrationException("Error copying " + resource + " to " + toFile.getAbsolutePath() + ". Cause: " + e, e);
- }
- }
-
- private void ensureDirectoryIsEmpty(File path) {
- String[] list = path.list();
- if (list.length != 0) {
- for (String entry : list) {
- if (!entry.startsWith(".")) {
- throw new MigrationException("Directory must be empty (.svn etc allowed): " + path.getAbsolutePath());
- }
- }
- }
- }
-
- private void createDirectoryIfNecessary(File path) {
- if (!path.exists()) {
- File parent = new File(path.getParent());
- 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.");
- }
- }
- }
-
- private String parsePlaceholders(String string, Map<String, String> variables) {
- final String OPEN = "${";
- final String CLOSE = "}";
- String newString = string;
- if (newString != null && variables != null) {
- int start = newString.indexOf(OPEN);
- int end = newString.indexOf(CLOSE);
-
- while (start > -1 && end > start) {
- String prepend = newString.substring(0, start);
- String append = newString.substring(end + CLOSE.length());
- String propName = newString.substring(start + OPEN.length(), end);
- String propValue = variables.get(propName);
- if (propValue == null) {
- newString = prepend + append;
- } else {
- newString = prepend + propValue + append;
- }
- start = newString.indexOf(OPEN);
- end = newString.indexOf(CLOSE);
- }
- }
- 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();
- String driver = props.getProperty("driver");
- String url = props.getProperty("url");
- String username = props.getProperty("username");
- String password = props.getProperty("password");
- return new ScriptRunner(driver, url, username, password, false, !force);
- } catch (Exception e) {
- throw new MigrationException("Error creating ScriptRunner. Cause: " + e, e);
- }
- }
-
- 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();
- props.load(new FileInputStream(file));
- return props;
- } catch (IOException e) {
- throw new MigrationException("Error loading environment properties. Cause: " + e, e);
- }
- }
-
}
Modified: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/ScriptRunner.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/ScriptRunner.java?rev=688202&r1=688201&r2=688202&view=diff
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/ScriptRunner.java (original)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/ScriptRunner.java Fri Aug 22 14:48:30 2008
@@ -5,9 +5,6 @@
import java.io.*;
import java.sql.*;
-/**
- * Utility to run database scripts
- */
public class ScriptRunner {
private static final String DEFAULT_DELIMITER = ";";
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/BaseCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/BaseCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/BaseCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/BaseCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,208 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.Change;
+import org.apache.ibatis.migration.MigrationException;
+import org.apache.ibatis.migration.ScriptRunner;
+import org.apache.ibatis.adhoc.AdHocExecutor;
+import org.apache.ibatis.io.Resources;
+
+import java.util.*;
+import java.sql.*;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.io.*;
+
+public abstract class BaseCommand implements Command {
+
+ protected static final PrintStream out = System.out;
+
+ protected File repository;
+ protected String environment;
+ protected boolean force;
+
+ protected BaseCommand(File repository, String environment, boolean force) {
+ this.repository = repository;
+ this.environment = environment;
+ this.force = force;
+ }
+
+
+ protected Change parseChangeFromFilename(String filename) {
+ 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);
+ }
+ }
+
+ protected List<Change> getChangelog() {
+ AdHocExecutor executor = getAdHocExecutor();
+ try {
+ 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()));
+ }
+ return changes;
+ } catch (SQLException e) {
+ throw new MigrationException("Error querying last applied migration. Cause: " + e, e);
+ } finally {
+ executor.closeConnection();
+ }
+ }
+
+ protected Change getLastChange() {
+ List<Change> changelog = getChangelog();
+ return changelog.get(changelog.size() - 1);
+ }
+
+ protected boolean changelogExists() {
+ AdHocExecutor executor = getAdHocExecutor();
+ try {
+ executor.selectAll("select ID, DESCRIPTION from CHANGELOG");
+ return true;
+ } catch (SQLException e) {
+ return false;
+ } finally {
+ executor.closeConnection();
+ }
+ }
+
+ protected String horizontalLine(String caption, int length) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("==========");
+ if (caption.length() > 0) {
+ caption = " " + caption + " ";
+ builder.append(caption);
+ }
+ for (int i = 0; i < length - caption.length(); i++) {
+ builder.append("=");
+ }
+ return builder.toString();
+ }
+
+ protected String getTimestampAsString() {
+ try {
+ // Ensure that two subsequent calls are less likely to return the same value.
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ //ignore
+ }
+ return new SimpleDateFormat("yyyyMMddHHmmss").format(new java.sql.Date(System.currentTimeMillis()));
+ }
+
+ protected void copyResourceTo(String resource, File toFile) {
+ copyResourceTo(resource, toFile, null);
+ }
+
+ protected void copyResourceTo(String resource, File toFile, Map<String, String> variables) {
+ out.println("Creating: " + toFile.getName());
+ try {
+ LineNumberReader reader = new LineNumberReader(Resources.getResourceAsReader(this.getClass().getClassLoader(), resource));
+ try {
+ PrintWriter writer = new PrintWriter(new FileWriter(toFile));
+ try {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ line = parsePlaceholders(line, variables);
+ writer.println(line);
+ }
+ } finally {
+ writer.close();
+ }
+ } finally {
+ reader.close();
+ }
+ } catch (IOException e) {
+ throw new MigrationException("Error copying " + resource + " to " + toFile.getAbsolutePath() + ". Cause: " + e, e);
+ }
+ }
+
+ protected 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");
+ return new AdHocExecutor(driver, url, username, password, false);
+ }
+
+ protected ScriptRunner getScriptRunner() {
+ try {
+ Properties props = getEnvironmentProperties();
+ String driver = props.getProperty("driver");
+ String url = props.getProperty("url");
+ String username = props.getProperty("username");
+ String password = props.getProperty("password");
+ ScriptRunner scriptRunner = new ScriptRunner(driver, url, username, password, false, !force);
+ PrintWriter outWriter = new PrintWriter(out);
+ scriptRunner.setLogWriter(outWriter);
+ scriptRunner.setErrorLogWriter(outWriter);
+ return scriptRunner;
+ } catch (Exception e) {
+ throw new MigrationException("Error creating ScriptRunner. Cause: " + e, e);
+ }
+ }
+
+ protected File repositoryFile(String fileName) {
+ return new File(repository.getAbsolutePath() + File.separator + fileName);
+ }
+
+ protected File environmentFile() {
+ return repositoryFile(environment + ".properties");
+ }
+
+ protected 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();
+ props.load(new FileInputStream(file));
+ return props;
+ } catch (IOException e) {
+ throw new MigrationException("Error loading environment properties. Cause: " + e, e);
+ }
+ }
+
+ private String parsePlaceholders(String string, Map<String, String> variables) {
+ final String OPEN = "${";
+ final String CLOSE = "}";
+ String newString = string;
+ if (newString != null && variables != null) {
+ int start = newString.indexOf(OPEN);
+ int end = newString.indexOf(CLOSE);
+
+ while (start > -1 && end > start) {
+ String prepend = newString.substring(0, start);
+ String append = newString.substring(end + CLOSE.length());
+ String propName = newString.substring(start + OPEN.length(), end);
+ String propValue = variables.get(propName);
+ if (propValue == null) {
+ newString = prepend + append;
+ } else {
+ newString = prepend + propValue + append;
+ }
+ start = newString.indexOf(OPEN);
+ end = newString.indexOf(CLOSE);
+ }
+ }
+ return newString;
+ }
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/Command.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/Command.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/Command.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/Command.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,5 @@
+package org.apache.ibatis.migration.commands;
+
+public interface Command {
+ void execute (String... params);
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/InitializeCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/InitializeCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/InitializeCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/InitializeCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,46 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.MigrationException;
+
+import java.io.File;
+
+public class InitializeCommand extends BaseCommand {
+
+ public InitializeCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute(String... args) {
+ 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!");
+ }
+
+ protected void ensureDirectoryIsEmpty(File path) {
+ String[] list = path.list();
+ if (list.length != 0) {
+ for (String entry : list) {
+ if (!entry.startsWith(".")) {
+ throw new MigrationException("Directory must be empty (.svn etc allowed): " + path.getAbsolutePath());
+ }
+ }
+ }
+ }
+
+ protected void createDirectoryIfNecessary(File path) {
+ if (!path.exists()) {
+ File parent = new File(path.getParent());
+ 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.");
+ }
+ }
+ }
+
+
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/NewCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/NewCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/NewCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/NewCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,33 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.MigrationException;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+public class NewCommand extends BaseCommand {
+
+ public NewCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute (String... params) {
+ if (paramsEmpty(params)) {
+ throw new MigrationException("No description specified for new migration.");
+ }
+ String description = params[0];
+ Map<String, String> variables = new HashMap<String, String>();
+ variables.put("description", description);
+ existingEnvironmentFile();
+ String filename = getTimestampAsString() + "_" + description.replace(' ', '_') + ".sql";
+ copyResourceTo("org/apache/ibatis/migration/template_migration.sql", repositoryFile(filename), variables);
+ out.println("Done!");
+ }
+
+ protected boolean paramsEmpty(String... params) {
+ return params == null || params.length < 1 || params[0] == null || params[0].length() < 1;
+ }
+
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/RunCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/RunCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/RunCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/RunCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,55 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.Change;
+import org.apache.ibatis.migration.MigrationReader;
+import org.apache.ibatis.migration.ScriptRunner;
+import org.apache.ibatis.migration.MigrationException;
+import org.apache.ibatis.adhoc.AdHocExecutor;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Arrays;
+import java.sql.SQLException;
+
+public class RunCommand extends BaseCommand {
+
+ public RunCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute(String... params) {
+ try {
+ String[] filenames = repository.list();
+ Arrays.sort(filenames);
+ Change lastChange = null;
+ if (changelogExists()) {
+ lastChange = getLastChange();
+ }
+ for (String filename : filenames) {
+ if (filename.endsWith(".sql")) {
+ Change change = parseChangeFromFilename(filename);
+ if (lastChange == null || change.getId().compareTo(lastChange.getId()) > 0) {
+ out.println(horizontalLine("Applying: " + filename, 80));
+ ScriptRunner runner = getScriptRunner();
+ runner.runScript(new MigrationReader(new FileReader(repositoryFile(filename)), false));
+ insertChangelog(change);
+ }
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("<Description>. Cause: " + e, e);
+ }
+ }
+
+ protected void insertChangelog(Change change) {
+ AdHocExecutor executor = getAdHocExecutor();
+ try {
+ 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();
+ }
+ }
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/StatusCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/StatusCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/StatusCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/StatusCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,27 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.Change;
+
+import java.util.List;
+import java.io.File;
+
+public class StatusCommand extends BaseCommand {
+
+ public StatusCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute(String... params) {
+ 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.");
+ }
+ }
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/UndoCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/UndoCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/UndoCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/UndoCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,68 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.Change;
+import org.apache.ibatis.migration.MigrationReader;
+import org.apache.ibatis.migration.ScriptRunner;
+import org.apache.ibatis.migration.MigrationException;
+import org.apache.ibatis.adhoc.AdHocExecutor;
+
+import java.io.File;
+import java.io.FileReader;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.sql.SQLException;
+
+public class UndoCommand extends BaseCommand {
+
+ public UndoCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute(String... params) {
+ try {
+ String[] filenames = repository.list();
+ reverse(filenames);
+ Change lastChange = getLastChange();
+ for (String filename : filenames) {
+ if (filename.endsWith(".sql")) {
+ Change change = parseChangeFromFilename(filename);
+ 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) {
+ throw new RuntimeException("<Description>. Cause: " + e, e);
+ }
+ }
+
+ protected void deleteChange(Change change) {
+ AdHocExecutor executor = getAdHocExecutor();
+ try {
+ 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();
+ }
+ }
+
+
+
+ protected void reverse(Comparable[] comparable) {
+ Arrays.sort(comparable, new Comparator() {
+ public int compare(Object o1, Object o2) {
+ return ((Comparable) o2).compareTo(o1);
+ }
+ });
+ }
+
+}
Added: ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/VersionCommand.java
URL: http://svn.apache.org/viewvc/ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/VersionCommand.java?rev=688202&view=auto
==============================================================================
--- ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/VersionCommand.java (added)
+++ ibatis/trunk/java/ibatis-3/ibatis-3-core/src/main/java/org/apache/ibatis/migration/commands/VersionCommand.java Fri Aug 22 14:48:30 2008
@@ -0,0 +1,22 @@
+package org.apache.ibatis.migration.commands;
+
+import org.apache.ibatis.migration.Change;
+import org.apache.ibatis.migration.ScriptRunner;
+import org.apache.ibatis.migration.MigrationReader;
+
+import java.math.BigInteger;
+import java.io.FileReader;
+import java.io.File;
+import java.util.List;
+
+public class VersionCommand extends BaseCommand {
+
+ public VersionCommand(File repository, String environment, boolean force) {
+ super(repository, environment, force);
+ }
+
+ public void execute(String... params) {
+ out.println("not implemented");
+ }
+
+}