All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.puresoltechnologies.genesis.tracker.cassandra.CassandraTransformationTracker Maven / Gradle / Ivy

The newest version!
package com.puresoltechnologies.genesis.tracker.cassandra;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.URL;
import java.util.Date;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.puresoltechnologies.commons.misc.hash.HashId;
import com.puresoltechnologies.commons.misc.hash.HashUtilities;
import com.puresoltechnologies.genesis.commons.ProvidedVersionRange;
import com.puresoltechnologies.genesis.commons.SequenceMetadata;
import com.puresoltechnologies.genesis.commons.TransformationException;
import com.puresoltechnologies.genesis.commons.TransformationMetadata;
import com.puresoltechnologies.genesis.commons.cassandra.CassandraUtils;
import com.puresoltechnologies.genesis.commons.cassandra.ReplicationStrategy;
import com.puresoltechnologies.genesis.tracker.spi.Severity;
import com.puresoltechnologies.genesis.tracker.spi.TransformationTracker;
import com.puresoltechnologies.versioning.Version;

/**
 * This is the default migration tracker for Purifinity which puts everything
 * into Cassandra.
 * 
 * @author Rick-Rainer Ludwig
 */
public class CassandraTransformationTracker implements TransformationTracker {

	private static final Logger logger = LoggerFactory
			.getLogger(CassandraTransformationTracker.class);

	public static final String DEFAULT_CASSANDRA_HOST_NAME = "localhost";
	public static final int DEFAULT_CASSANDRA_PORT = 9042;
	public static final String DEFAULT_KEYSPACE_NAME = "genesis";
	public static final int DEFAULT_REPLICATION_FACTOR = 1;
	public static final String DEFAULT_REPLICATION_STRATEGY = ReplicationStrategy.SIMPLE_STRATEGY
			.getStrategyName().toString();
	public static final String CHANGELOG_TABLE = "changelog";
	public static final String MIGRATIONLOG_TABLE = "migrationlog";
	public static final String LAST_TRANSFORMATIONS_TABLE = "last_transformations";

	private PreparedStatement preparedInsertStatement = null;
	private PreparedStatement preparedSelectStatement = null;
	private PreparedStatement preparedDropComponentStatement = null;
	private PreparedStatement preparedLoggingStatement = null;
	private PreparedStatement preparedInsertLastTransformationStatement = null;
	private PreparedStatement preparedSelectLastTransformationStatement = null;
	private PreparedStatement preparedDropComponentLastTransformtaionStatement = null;

	private Cluster cluster = null;
	private Session session = null;

	private String host = DEFAULT_CASSANDRA_HOST_NAME;
	private int port = DEFAULT_CASSANDRA_PORT;
	private String keyspace = DEFAULT_KEYSPACE_NAME;
	private int replicationFactor = DEFAULT_REPLICATION_FACTOR;
	private String replicationStrategy = DEFAULT_REPLICATION_STRATEGY;

	@Override
	public void open() throws TransformationException {
		loadAlternateConfigIfPresent();
		connect();
		prepareKeyspace();
	}

	private void loadAlternateConfigIfPresent() {
		URL configuration = getClass().getResource(
				"/genesis.tracker.cassandra.properties");
		if (configuration != null) {
			try (InputStream stream = configuration.openStream()) {
				Properties properties = new Properties();
				properties.load(stream);
				host = properties.getProperty("host",
						DEFAULT_CASSANDRA_HOST_NAME);
				keyspace = properties.getProperty("keyspace",
						DEFAULT_KEYSPACE_NAME);
				String portString = properties.getProperty("port",
						Integer.toString(DEFAULT_CASSANDRA_PORT));
				port = Integer.parseInt(portString);
				String replicationFactorString = properties.getProperty(
						"replication.factor",
						Integer.toString(DEFAULT_REPLICATION_FACTOR));
				replicationFactor = Integer.parseInt(replicationFactorString);
				replicationStrategy = properties.getProperty(
						"replication.strategy", DEFAULT_REPLICATION_STRATEGY);
			} catch (IOException e) {
				System.err
						.println("Warning: A configuration file for Cassandra Tracker was found, but could not be opened.");
			}
		}
	}

	private void connect() {
		if (cluster != null) {
			throw new IllegalStateException(
					"Cluster for migration tracker was already connected.");
		}
		cluster = CassandraUtils.connectCluster(host, port);
		if (session != null) {
			throw new IllegalStateException(
					"Session for migration tracker was already opened.");
		}
		session = CassandraUtils.connectWithoutKeyspace(cluster);
	}

	private void prepareKeyspace() throws TransformationException {
		Metadata clusterMetadata = cluster.getMetadata();
		KeyspaceMetadata keyspaceMetadata = clusterMetadata
				.getKeyspace(keyspace);
		if (keyspaceMetadata == null) {
			logger.info("Keyspace for Cassandra migration is missing. Needs to be created...");
			session.execute("CREATE KEYSPACE " + keyspace + " WITH " + //
					"replication " + "= {" + //
					"'class':'" + replicationStrategy + "', " + //
					"'replication_factor':" + replicationFactor + "};");
			keyspaceMetadata = clusterMetadata.getKeyspace(keyspace);
			if (keyspaceMetadata == null) {
				throw new TransformationException("Could not create keyspace '"
						+ keyspace + "'.");
			}
			logger.info("Keyspace for Cassandra migration created.");
		}
		if (keyspaceMetadata
				.getTable(CassandraTransformationTracker.CHANGELOG_TABLE) == null) {
			logger.info("ChangeLog table for Cassandra migration is missing. Needs to be created...");
			session.execute("CREATE TABLE " + keyspace + "."
					+ CassandraTransformationTracker.CHANGELOG_TABLE + " ("
					+ "time timestamp, " + "component varchar, "
					+ "machine varchar, " + "version varchar, "
					+ "command varchar, " + "developer varchar, "
					+ "comment varchar, " + "hashid varchar, "
					+ "PRIMARY KEY(component, machine, version, command));");
			logger.info("ChangeLog table for Cassandra migration created.");
		}
		if (keyspaceMetadata
				.getTable(CassandraTransformationTracker.MIGRATIONLOG_TABLE) == null) {
			logger.info("MigrationLog table for Cassandra migration is missing. Needs to be created...");
			session.execute("CREATE TABLE " + keyspace + "."
					+ CassandraTransformationTracker.MIGRATIONLOG_TABLE + " ("
					+ "time timestamp, " + "severity varchar, "
					+ "machine varchar, " + "thread varchar, "
					+ "message varchar, " + "exception_type varchar, "
					+ "exception_message varchar, " + "stacktrace varchar, "
					+ "PRIMARY KEY(time, machine, thread, message));");
			logger.info("MigrationLog table for Cassandra migration created.");
		}
		if (keyspaceMetadata
				.getTable(CassandraTransformationTracker.LAST_TRANSFORMATIONS_TABLE) == null) {
			logger.info("LastTransformations table for Cassandra migration is missing. Needs to be created...");
			session.execute("CREATE TABLE " + keyspace + "."
					+ CassandraTransformationTracker.LAST_TRANSFORMATIONS_TABLE
					+ " (" + "time timestamp, " + "component varchar, "
					+ "machine varchar, " + "start_version varchar, "
					+ "target_version varchar, " + "next_version varchar, "
					+ "command varchar, " + "developer varchar, "
					+ "comment varchar, " + "hashid varchar, "
					+ "PRIMARY KEY(component, machine));");
			logger.info("LastTransformations table for Cassandra migration created.");
		}
	}

	private void createPreparedStatements(Session session) {
		if (preparedInsertStatement == null) {
			preparedInsertStatement = session
					.prepare("INSERT INTO "
							+ keyspace
							+ "."
							+ CHANGELOG_TABLE
							+ " (time, component, machine, version, command, developer, comment, hashid)"
							+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
			preparedInsertStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedSelectStatement == null) {
			preparedSelectStatement = session.prepare("SELECT * FROM "
					+ keyspace + "." + CHANGELOG_TABLE + " WHERE component=?"
					+ " AND machine=?" + " AND version=?" + " AND command=?"
					+ ";");
			preparedSelectStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedDropComponentStatement == null) {
			preparedDropComponentStatement = session.prepare("DELETE FROM "
					+ keyspace + "." + CHANGELOG_TABLE
					+ " WHERE component=? AND machine=?;");
			preparedDropComponentStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedLoggingStatement == null) {
			preparedLoggingStatement = session
					.prepare("INSERT INTO "
							+ keyspace
							+ "."
							+ MIGRATIONLOG_TABLE
							+ " (time, severity, machine, thread, message, exception_type, exception_message, stacktrace)"
							+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
			preparedLoggingStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedInsertLastTransformationStatement == null) {
			preparedInsertLastTransformationStatement = session
					.prepare("INSERT INTO "
							+ keyspace
							+ "."
							+ LAST_TRANSFORMATIONS_TABLE
							+ " (time, component, machine, start_version, target_version, next_version, command, developer, comment, hashid)"
							+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
			preparedInsertLastTransformationStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedSelectLastTransformationStatement == null) {
			preparedSelectLastTransformationStatement = session
					.prepare("SELECT * FROM " + keyspace + "."
							+ LAST_TRANSFORMATIONS_TABLE + " WHERE component=?"
							+ " AND machine=?" + ";");
			preparedSelectLastTransformationStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
		if (preparedDropComponentLastTransformtaionStatement == null) {
			preparedDropComponentLastTransformtaionStatement = session
					.prepare("DELETE FROM " + keyspace + "."
							+ LAST_TRANSFORMATIONS_TABLE
							+ " WHERE component=? AND machine=?;");
			preparedDropComponentLastTransformtaionStatement
					.setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
		}
	}

	@Override
	public void close() {
		preparedInsertStatement = null;
		preparedSelectStatement = null;
		preparedDropComponentStatement = null;
		try {
			session.close();
			session = null;
		} finally {
			cluster.close();
			cluster = null;
		}
	}

	@Override
	public void trackMigration(InetAddress machine,
			TransformationMetadata metadata) throws TransformationException {
		if ((preparedInsertStatement == null)
				|| (preparedInsertLastTransformationStatement == null)) {
			createPreparedStatements(session);
		}
		try {
			// Tracking...
			HashId hashId = HashUtilities.createHashId(metadata.getCommand());
			BoundStatement boundStatement = preparedInsertStatement.bind(
					new Date(), metadata.getComponentName(), machine
							.getHostAddress(), metadata.getTargetVersion()
							.toString(), metadata.getCommand(), metadata
							.getDeveloper(), metadata.getComment(), hashId
							.toString());
			session.execute(boundStatement);
			// Last Transformations...
			String nextVersionString = metadata.getNextVersion() != null ? metadata
					.getNextVersion().toString() : "";
			boundStatement = preparedInsertLastTransformationStatement.bind(
					new Date(), metadata.getComponentName(), machine
							.getHostAddress(), metadata.getStartVersion()
							.toString(),
					metadata.getTargetVersion().toString(), nextVersionString,
					metadata.getCommand(), metadata.getDeveloper(), metadata
							.getComment(), hashId.toString());
			session.execute(boundStatement);
		} catch (IOException e) {
			throw new TransformationException(
					"Could not track migration step.", e);
		}
	}

	@Override
	public boolean wasMigrated(String component, InetAddress machine,
			Version version, String command) {
		if (preparedSelectStatement == null) {
			createPreparedStatements(session);
		}
		BoundStatement boundStatement = preparedSelectStatement.bind(component,
				machine.getHostAddress(), version.toString(), command);
		ResultSet result = session.execute(boundStatement);
		return result.iterator().hasNext();
	}

	@Override
	public void dropComponentHistory(String component, InetAddress machine) {
		if ((preparedDropComponentStatement == null)
				|| (preparedDropComponentLastTransformtaionStatement == null)) {
			createPreparedStatements(session);
		}
		BoundStatement boundStatement = preparedDropComponentStatement.bind(
				component, machine.getHostAddress());
		session.execute(boundStatement);
		boundStatement = preparedDropComponentLastTransformtaionStatement.bind(
				component, machine.getHostAddress());
		session.execute(boundStatement);
	}

	@Override
	public void log(Date time, Severity severity, InetAddress machine,
			Thread thread, String message, Throwable cause) {
		if (preparedLoggingStatement == null) {
			createPreparedStatements(session);
		}
		if (cause == null) {
			BoundStatement boundStatement = preparedLoggingStatement.bind(time,
					severity.name(), machine.getHostAddress().toString(),
					thread.getName(), message, "", "", "");
			session.execute(boundStatement);
		} else {
			BoundStatement boundStatement = preparedLoggingStatement.bind(time,
					severity.name(), machine.getHostAddress().toString(),
					thread.getName(), message, cause.getClass().getName(),
					cause.getMessage(), cause.toString());
			session.execute(boundStatement);
		}
	}

	@Override
	public TransformationMetadata getLastTransformationMetadata(
			String component, InetAddress machine) {
		if (preparedSelectLastTransformationStatement == null) {
			createPreparedStatements(session);
		}
		BoundStatement boundStatement = preparedSelectLastTransformationStatement
				.bind(component, machine.getHostAddress());
		ResultSet resultSet = session.execute(boundStatement);
		Row next = resultSet.iterator().next();
		if (next == null) {
			return null;
		}
		Version startVersion = Version.valueOf(next.getString("start_version"));
		Version targetVersion = Version.valueOf(next
				.getString("target_version"));
		String nextVersionString = next.getString("next_version");
		Version nextVersion;
		if ((nextVersionString != null) && (!nextVersionString.isEmpty())) {
			nextVersion = Version.valueOf(nextVersionString);
		} else {
			nextVersion = null;
		}
		String developer = next.getString("developer");
		String command = next.getString("command");
		String comment = next.getString("comment");
		SequenceMetadata sequenceMetadata = new SequenceMetadata(component,
				startVersion, new ProvidedVersionRange(targetVersion,
						nextVersion));
		return new TransformationMetadata(sequenceMetadata, developer, command,
				comment);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy