package com.contrastsecurity.cassandra.migration; import com.contrastsecurity.cassandra.migration.action.Initialize; import com.contrastsecurity.cassandra.migration.action.Migrate; import com.contrastsecurity.cassandra.migration.action.Validate; import com.contrastsecurity.cassandra.migration.config.Keyspace; import com.contrastsecurity.cassandra.migration.config.MigrationConfigs; import com.contrastsecurity.cassandra.migration.config.ScriptsLocations; import com.contrastsecurity.cassandra.migration.dao.SchemaVersionDAO; import com.contrastsecurity.cassandra.migration.info.MigrationInfoService; import com.contrastsecurity.cassandra.migration.info.MigrationVersion; import com.contrastsecurity.cassandra.migration.logging.Log; import com.contrastsecurity.cassandra.migration.logging.LogFactory; import com.contrastsecurity.cassandra.migration.resolver.CompositeMigrationResolver; import com.contrastsecurity.cassandra.migration.resolver.MigrationResolver; import com.contrastsecurity.cassandra.migration.utils.VersionPrinter; import com.datastax.driver.core.Host; import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.Metadata; import com.datastax.driver.core.Session; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.util.List; public class CassandraMigration { private static final Log LOG = LogFactory.getLog(CassandraMigration.class); private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); private Keyspace keyspace; private MigrationConfigs configs; public CassandraMigration() { this.keyspace = new Keyspace(); this.configs = new MigrationConfigs(); } public ClassLoader getClassLoader() { return classLoader; } /** * Sets the ClassLoader to use for resolving migrations on the classpath. * * @param classLoader The ClassLoader to use for resolving migrations on the classpath. (default: Thread.currentThread().getContextClassLoader() ) */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public Keyspace getKeyspace() { return keyspace; } public void setKeyspace(Keyspace keyspace) { this.keyspace = keyspace; } public MigrationConfigs getConfigs() { return configs; } private MigrationResolver createMigrationResolver() { return new CompositeMigrationResolver(classLoader, new ScriptsLocations(configs.getScriptsLocations()), configs.getEncoding()); } public int migrate() { return execute(new Action<Integer>() { public Integer execute(Session session) { new Initialize().run(session, keyspace, MigrationVersion.CURRENT.getTable()); MigrationResolver migrationResolver = createMigrationResolver(); SchemaVersionDAO schemaVersionDAO = new SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.getTable()); Migrate migrate = new Migrate(migrationResolver, configs.getTarget(), schemaVersionDAO, session, keyspace.getCluster().getUsername(), configs.isAllowOutOfOrder()); return migrate.run(); } }); } public MigrationInfoService info() { return execute(new Action<MigrationInfoService>() { public MigrationInfoService execute(Session session) { MigrationResolver migrationResolver = createMigrationResolver(); SchemaVersionDAO schemaVersionDAO = new SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.getTable()); MigrationInfoService migrationInfoService = new MigrationInfoService(migrationResolver, schemaVersionDAO, configs.getTarget(), false, true); migrationInfoService.refresh(); return migrationInfoService; } }); } public void validate() { String validationError = execute(new Action<String>() { @Override public String execute(Session session) { MigrationResolver migrationResolver = createMigrationResolver(); SchemaVersionDAO schemaVersionDao = new SchemaVersionDAO(session, keyspace, MigrationVersion.CURRENT.getTable()); Validate validate = new Validate(migrationResolver, schemaVersionDao, configs.getTarget(), true, false); return validate.run(); } }); if (validationError != null) { throw new CassandraMigrationException("Validation failed. " + validationError); } } public void baseline() { //TODO throw new NotImplementedException(); } private String getConnectionInfo(Metadata metadata) { StringBuilder sb = new StringBuilder(); sb.append("Connected to cluster: "); sb.append(metadata.getClusterName()); sb.append("\n"); for (Host host : metadata.getAllHosts()) { sb.append("Data center: "); sb.append(host.getDatacenter()); sb.append("; Host: "); sb.append(host.getAddress()); } return sb.toString(); } <T> T execute(Action<T> action) { T result; VersionPrinter.printVersion(classLoader); com.datastax.driver.core.Cluster cluster = null; Session session = null; try { if (null == keyspace) throw new IllegalArgumentException("Unable to establish Cassandra session. Keyspace is not configured."); if (null == keyspace.getCluster()) throw new IllegalArgumentException("Unable to establish Cassandra session. Cluster is not configured."); com.datastax.driver.core.Cluster.Builder builder = new com.datastax.driver.core.Cluster.Builder(); builder.addContactPoints(keyspace.getCluster().getContactpoints()).withPort(keyspace.getCluster().getPort()); if (null != keyspace.getCluster().getUsername() && !keyspace.getCluster().getUsername().trim().isEmpty()) { if (null != keyspace.getCluster().getPassword() && !keyspace.getCluster().getPassword().trim().isEmpty()) { builder.withCredentials(keyspace.getCluster().getUsername(), keyspace.getCluster().getPassword()); } else { throw new IllegalArgumentException("Password must be provided with username."); } } cluster = builder.build(); Metadata metadata = cluster.getMetadata(); LOG.info(getConnectionInfo(metadata)); session = cluster.newSession(); if (null == keyspace.getName() || keyspace.getName().trim().length() == 0) throw new IllegalArgumentException("Keyspace not specified."); List<KeyspaceMetadata> keyspaces = metadata.getKeyspaces(); boolean keyspaceExists = false; for (KeyspaceMetadata keyspaceMetadata : keyspaces) { if (keyspaceMetadata.getName().equalsIgnoreCase(keyspace.getName())) keyspaceExists = true; } if (keyspaceExists) session.execute("USE " + keyspace.getName()); else throw new CassandraMigrationException("Keyspace: " + keyspace.getName() + " does not exist."); result = action.execute(session); } finally { if (null != session && !session.isClosed()) try { session.close(); } catch(Exception e) { LOG.warn("Error closing Cassandra session"); } if (null != cluster && !cluster.isClosed()) try { cluster.close(); } catch(Exception e) { LOG.warn("Error closing Cassandra cluster"); } } return result; } interface Action<T> { T execute(Session session); } }