Java tutorial
/** * Copyright (C) 2012 Ness Computing, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.nesscomputing.migratory.mojo.database; import static java.lang.String.format; import java.io.File; import java.io.StringReader; import java.net.URI; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import com.google.common.base.Charsets; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.commons.configuration.CombinedConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.SystemConfiguration; import org.apache.commons.configuration.tree.OverrideCombiner; import org.apache.commons.lang3.StringUtils; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.skife.config.CommonsConfigSource; import org.skife.config.ConfigurationObjectFactory; import org.skife.jdbi.v2.DBI; import com.nesscomputing.logging.Log; import com.nesscomputing.migratory.MigratoryConfig; import com.nesscomputing.migratory.MigratoryOption; import com.nesscomputing.migratory.loader.FileLoader; import com.nesscomputing.migratory.loader.HttpLoader; import com.nesscomputing.migratory.loader.JarLoader; import com.nesscomputing.migratory.loader.LoaderManager; import com.nesscomputing.migratory.maven.ConfigureLog4j; import com.nesscomputing.migratory.mojo.database.util.DBIConfig; import com.nesscomputing.migratory.mojo.database.util.InitialConfig; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public abstract class AbstractDatabaseMojo extends AbstractMojo { private static final String[] MUST_EXIST = new String[] { "default.base", "default.root_url", "default.root_user", "default.root_password", "default.user", "default.password" }; private static final String[] MUST_NOT_BE_EMPTY = new String[] { "default.base", "default.root_url", "default.root_user", "default.user" }; public static final String MIGRATORY_PROPERTIES_FILE = ".migratory.properties"; private static final Log LOG = Log.findLog(); private ConfigurationObjectFactory factory; protected DBIConfig rootDBIConfig; protected Configuration config; protected InitialConfig initialConfig; protected MigratoryConfig migratoryConfig; protected LoaderManager loaderManager; protected MigratoryOption[] optionList; private void stateCheck() throws MojoExecutionException { StringBuilder sb = new StringBuilder(); sb.append(rootDBIConfig == null ? "rootDBIConfig is null, " : ""); sb.append(config == null ? "config is null, " : ""); sb.append(initialConfig == null ? "initialConfig is null, " : ""); sb.append(migratoryConfig == null ? "migratoryConfig is null, " : ""); sb.append(loaderManager == null ? "loaderManager is null, " : ""); sb.append(optionList == null ? "optionList is null, " : ""); if (sb.length() > 0) { throw new MojoExecutionException(format("Internal error(s) (%s), refusing to run mojo !", sb)); } } /** * @parameter expression="${manifest.url}" */ protected String manifestUrl = null; /** * @parameter expression="${manifest.name}" */ private String manifestName = null; /** * @parameter expression="${options}" */ @SuppressFBWarnings("UWF_UNWRITTEN_FIELD") private String options; @Override public final void execute() throws MojoExecutionException, MojoFailureException { ConfigureLog4j.start(this); try { // Load the default manifest information. // final CombinedConfiguration config = new CombinedConfiguration(new OverrideCombiner()); // everything can be overridden by system properties. config.addConfiguration(new SystemConfiguration(), "systemProperties"); final String userHome = System.getProperty("user.home"); if (userHome != null) { final File propertyFile = new File(userHome, MIGRATORY_PROPERTIES_FILE); if (propertyFile.exists() && propertyFile.canRead() && propertyFile.isFile()) { config.addConfiguration(new PropertiesConfiguration(propertyFile)); } } final ConfigurationObjectFactory initialConfigFactory = new ConfigurationObjectFactory( new CommonsConfigSource(config)); // Load the initial config from the local config file this.initialConfig = initialConfigFactory.build(InitialConfig.class); if (this.manifestUrl == null) { this.manifestUrl = initialConfig.getManifestUrl(); } if (this.manifestName == null) { this.manifestName = initialConfig.getManifestName(); } if (manifestUrl == null) { throw new MojoExecutionException( "no manifest url found (did you create a .migratory.properties file?)"); } if (manifestName == null) { throw new MojoExecutionException( "no manifest name found (did you create a .migratory.properties file?)"); } LOG.debug("Manifest URL: %s", manifestUrl); LOG.debug("Manifest Name: %s", manifestName); this.optionList = parseOptions(options); final StringBuilder location = new StringBuilder(manifestUrl); if (!this.manifestUrl.endsWith("/")) { location.append("/"); } // After here, the manifestUrl is guaranteed to have a / at the end! this.manifestUrl = location.toString(); location.append(manifestName); location.append(".manifest"); LOG.debug("Manifest Location: %s", location); final MigratoryConfig initialMigratoryConfig = initialConfigFactory.build(MigratoryConfig.class); final LoaderManager initialLoaderManager = createLoaderManager(initialMigratoryConfig); final String contents = initialLoaderManager.loadFile(URI.create(location.toString())); if (contents == null) { throw new MojoExecutionException( format("Could not load manifest '%s' from '%s'", manifestName, manifestUrl)); } // // Now add the contents of the manifest file to the configuration creating the // final configuration for building the sql migration sets. // final PropertiesConfiguration pc = new PropertiesConfiguration(); pc.load(new StringReader(contents)); config.addConfiguration(pc); if (!validateConfiguration(config)) { throw new MojoExecutionException( format("Manifest '%s' is not valid. Refusing to execute!", manifestName)); } this.config = config; this.factory = new ConfigurationObjectFactory(new CommonsConfigSource(config)); this.migratoryConfig = factory.build(MigratoryConfig.class); this.loaderManager = createLoaderManager(migratoryConfig); LOG.debug("Configuration: %s", this.config); this.rootDBIConfig = getDBIConfig(getPropertyName("default.root_")); stateCheck(); doExecute(); } catch (Exception e) { Throwables.propagateIfInstanceOf(e, MojoExecutionException.class); LOG.errorDebug(e, "While executing Mojo %s", this.getClass().getSimpleName()); throw new MojoExecutionException("Failure:", e); } finally { ConfigureLog4j.stop(this); } } /** * Executes this mojo. */ protected abstract void doExecute() throws Exception; protected String getPropertyName(final String name) { return initialConfig.getDefaultPropertyPrefix() + "." + name; } private DBIConfig getDBIConfig(final String dbiName) { return factory.buildWithReplacements(DBIConfig.class, ImmutableMap.of("_dbi_name", dbiName, "_prefix", initialConfig.getDefaultPropertyPrefix())); } protected DBIConfig getDBIConfigFor(final String database) { final DBIConfig baseConfig = getDBIConfig(getPropertyName(format("db.%s.", database))); final String dbUrl = (baseConfig.getDBUrl() != null) ? baseConfig.getDBUrl() : String.format(config.getString(getPropertyName("default.base")), database); return new DBIConfig() { @Override public String getDBDriverClass() { return baseConfig.getDBDriverClass(); } @Override public String getDBUser() { return baseConfig.getDBUser(); } @Override public String getDBPassword() { return baseConfig.getDBPassword(); } @Override public String getDBUrl() { return dbUrl; } @Override public String getDBTablespace() { return baseConfig.getDBTablespace(); } }; } protected DBI getDBIFor(final String database) throws Exception { return getDBIFor(getDBIConfigFor(database)); } protected DBI getDBIFor(final DBIConfig dbiConfig) throws Exception { if (dbiConfig.getDBDriverClass() != null) { Class.forName(dbiConfig.getDBDriverClass()); } return new DBI(dbiConfig.getDBUrl(), dbiConfig.getDBUser(), dbiConfig.getDBPassword()); } protected List<String> expandDatabaseList(final String databases) throws MojoExecutionException { final String[] databaseNames = StringUtils.stripAll(StringUtils.split(databases, ",")); if (databaseNames == null) { return Collections.<String>emptyList(); } final List<String> availableDatabases = getAvailableDatabases(); if (databaseNames.length == 1 && databaseNames[0].equalsIgnoreCase("all")) { return availableDatabases; } else { for (String database : databaseNames) { if (!availableDatabases.contains(database)) { throw new MojoExecutionException("Database " + database + " is unknown!"); } } return Arrays.asList(databaseNames); } } protected List<String> getAvailableDatabases() { final List<String> databaseList = Lists.newArrayList(); final Configuration dbConfig = config.subset(getPropertyName("db")); for (Iterator<?> it = dbConfig.getKeys(); it.hasNext();) { final String key = (String) it.next(); if (key.contains(".")) { continue; } databaseList.add(key); } return databaseList; } protected MigratoryOption[] parseOptions(final String options) { final String[] optionList = StringUtils.stripAll(StringUtils.split(options, ",")); if (optionList == null) { return new MigratoryOption[0]; } final MigratoryOption[] migratoryOptions = new MigratoryOption[optionList.length]; for (int i = 0; i < optionList.length; i++) { migratoryOptions[i] = MigratoryOption.valueOf(optionList[i].toUpperCase(Locale.ENGLISH)); } LOG.debug("Parsed %s into %s", options, migratoryOptions); return migratoryOptions; } protected Map<String, MigrationInformation> getAvailableMigrations(final String database) throws MojoExecutionException { final Map<String, MigrationInformation> availableMigrations = Maps.newHashMap(); addMigrations(getPropertyName("db." + database), availableMigrations); addMigrations(getPropertyName("default.personalities"), availableMigrations); return availableMigrations; } protected void addMigrations(final String property, final Map<String, MigrationInformation> availableMigrations) throws MojoExecutionException { final String[] personalities = StringUtils.stripAll(config.getStringArray(property)); for (String personality : personalities) { final String[] personalityParts = StringUtils.stripAll(StringUtils.split(personality, ":")); if (personalityParts == null || personalityParts.length < 1 || personalityParts.length > 2) { throw new MojoExecutionException("Personality " + personality + " is invalid."); } if (personalityParts.length == 1) { availableMigrations.put(personalityParts[0], new MigrationInformation(personalityParts[0], 0)); } else { availableMigrations.put(personalityParts[0], new MigrationInformation(personalityParts[0], Integer.parseInt(personalityParts[1], 10))); } } } protected static class MigrationInformation { private final String name; private final int priority; public MigrationInformation(final String name, final int priority) { this.name = name; this.priority = priority; } public String getName() { return name; } public int getPriority() { return priority; } } private boolean validateConfiguration(Configuration config) throws MojoExecutionException { boolean valid = true; for (String mustExist : MUST_EXIST) { final String propertyName = getPropertyName(mustExist); if (!config.containsKey(propertyName)) { LOG.error("The required property '%s' does not exist in the manifest.", propertyName); valid = false; } } for (String mustNotBeEmpty : MUST_NOT_BE_EMPTY) { final String propertyName = getPropertyName(mustNotBeEmpty); if (StringUtils.isBlank(config.getString(propertyName, null))) { LOG.error("The property '%s' must not be empty.", propertyName); valid = false; } } final String defaultBase = config.getString(getPropertyName("default.base"), ""); if (defaultBase.indexOf("%s") == -1 || defaultBase.indexOf("%s") != defaultBase.lastIndexOf("%s")) { LOG.error("The 'config.default.base' property must contain exactly one '%s' place holder!"); valid = false; } return valid; } private LoaderManager createLoaderManager(final MigratoryConfig migratoryConfig) { final LoaderManager loaderManager = new LoaderManager(); loaderManager.addLoader(new FileLoader(Charsets.UTF_8)); loaderManager.addLoader(new JarLoader(Charsets.UTF_8)); loaderManager.addLoader(new HttpLoader(migratoryConfig)); return loaderManager; } }