/**
* The MIT License (MIT)
*
* Copyright (c) 2013, AppMetr
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.appmetr.hercules;
import com.appmetr.hercules.annotations.Partitioned;
import com.appmetr.hercules.driver.DataDriver;
import com.appmetr.hercules.manager.EntityManager;
import com.appmetr.hercules.manager.IndexManager;
import com.appmetr.hercules.manager.WideEntityManager;
import com.appmetr.hercules.metadata.EntityMetadata;
import com.appmetr.hercules.metadata.EntityMetadataExtractor;
import com.appmetr.hercules.metadata.WideEntityMetadata;
import com.appmetr.hercules.metadata.WideEntityMetadataExtractor;
import com.appmetr.hercules.mutations.ExecutableMutation;
import com.appmetr.hercules.mutations.MutationsQueue;
import com.appmetr.hercules.partition.PartitioningStarter;
import com.google.inject.Inject;
import com.google.inject.Injector;
import me.prettyprint.hector.api.Cluster;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.ddl.ColumnFamilyDefinition;
import me.prettyprint.hector.api.ddl.ComparatorType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class Hercules {
protected Logger logger = LoggerFactory.getLogger(Hercules.class);
public static final int DEFAULT_BATCH_SIZE = 1000;
@Inject HerculesConfig config;
private Map<Class, EntityMetadata> entityClassMetadataCache = new HashMap<Class, EntityMetadata>();
private Map<Class, WideEntityMetadata> wideEntityClassMetadataCache = new HashMap<Class, WideEntityMetadata>();
@Inject EntityMetadataExtractor metadataExtractor;
@Inject WideEntityMetadataExtractor wideMetadataExtractor;
private Cluster cluster;
private Keyspace keyspace;
@Inject private Injector injector;
@Inject private DataDriver dataDriver;
@Inject private WideEntityManager wideEntityManager;
@Inject private EntityManager entityManager;
@Inject private IndexManager indexManager;
@Inject private MutationsQueue mutationsQueue;
@Inject private PartitioningStarter partitioningStarter;
public void init() {
cluster = dataDriver.getOrCreateCluster(config.getClusterName(), config.getCassandraHost(), config.getMaxActiveConnections(), config.getMaxConnectTimeMillis(), config.getCassandraThriftSocketTimeout());
keyspace = dataDriver.getOrCreateKeyspace(config.getKeyspaceName(), config.getReplicationFactor(), cluster);
initEntities();
new Thread(mutationsQueue).start();
new Thread(partitioningStarter).start();
}
public void shutdown() {
partitioningStarter.stop();
mutationsQueue.stop();
dataDriver.shutdownCluster(cluster);
}
public Set<String> getColumnFamilies() {
List<ColumnFamilyDefinition> columnFamilies = cluster.describeKeyspace(getKeyspaceName()).getCfDefs();
Set<String> columnFamiliesNames = new HashSet<String>();
for (ColumnFamilyDefinition cf : columnFamilies) {
columnFamiliesNames.add(cf.getName());
}
return columnFamiliesNames;
}
public boolean checkAndCreateColumnFamily(String cfName) {
return checkAndCreateColumnFamily(cfName, ComparatorType.UTF8TYPE);
}
public boolean checkAndCreateColumnFamily(String cfName, ComparatorType comparator) {
return dataDriver.checkAndCreateColumnFamily(cluster, config.getKeyspaceName(), cfName, comparator);
}
public boolean deleteColumnFamily(String cfName) {
return dataDriver.deleteColumnFamily(cluster, config.getKeyspaceName(), cfName);
}
private void initEntities() {
initPlainEntities();
initiateWideEntities();
}
private void initPlainEntities() {
logger.info("Initializing plain entity classes...");
checkAndCreateColumnFamily(EntityManager.PRIMARY_KEY_CF_NAME);
for (Class<?> entityClass : config.getEntityClasses()) {
logger.info("Extracting metadata for entity " + entityClass.getName());
EntityMetadata metadata = metadataExtractor.extract(entityClass);
entityClassMetadataCache.put(entityClass, metadata);
checkAndCreateColumnFamily(metadata.getColumnFamily(), metadata.getComparatorType());
}
//We should have extracted metadata before create indexes
for (EntityMetadata metadata : entityClassMetadataCache.values()) {
indexManager.checkAndCreateEntityIndexes(metadata);
}
}
private void initiateWideEntities() {
logger.info("Initializing wide entity classes...");
for (Class<?> wideEntityClass : config.getWideEntityClasses()) {
logger.info("Extracting metadata for wide entity " + wideEntityClass.getName());
WideEntityMetadata metadata = wideMetadataExtractor.extract(wideEntityClass);
wideEntityClassMetadataCache.put(wideEntityClass, metadata);
checkAndCreateColumnFamily(metadata.getColumnFamily(), metadata.getComparatorType());
}
}
public EntityMetadata getMetadata(Class entityClass) {
EntityMetadata meta = entityClassMetadataCache.get(entityClass);
if (meta == null) {
throw new RuntimeException("Can't find metadata for plain entity " + entityClass.getName());
}
return meta;
}
public WideEntityMetadata getWideMetadata(Class wideEntityClass) {
WideEntityMetadata meta = wideEntityClassMetadataCache.get(wideEntityClass);
if (meta == null) {
throw new RuntimeException("Can't find metadata for wide entity " + wideEntityClass.getName());
}
return meta;
}
public Set<ExecutableMutation> getPartitionMutations() {
final Set<String> columnFamilies = getColumnFamilies();
Set<ExecutableMutation> mutations = new HashSet<ExecutableMutation>();
for (final Class partitionEntityClass : config.getWideEntityClasses()) {
if (!partitionEntityClass.isAnnotationPresent(Partitioned.class)) {
continue;
}
for (String cfPartitionName : getWideEntityManager().getCFForPartitionCreation(partitionEntityClass)) {
String cfFullName = getWideMetadata(partitionEntityClass).getColumnFamily() + cfPartitionName;
if (!columnFamilies.contains(cfFullName)) {
mutations.add(new ExecutableMutation(ExecutableMutation.MutationType.CREATE, cfFullName) {
@Override public void execute() throws Exception {
checkAndCreateColumnFamily(getCfName(), getWideMetadata(partitionEntityClass).getComparatorType());
columnFamilies.add(getCfName());
logger.info("Created partition: " + getCfName());
}
@Override public void skipped() {
logger.info("Skipped partition creation: " + getCfName());
}
});
}
}
}
return mutations;
}
/*Getters and Setters*/
public Cluster getCluster() { return cluster; }
public Keyspace getKeyspace() { return keyspace; }
public String getKeyspaceName() { return config.getKeyspaceName(); }
public String getCassandraHost() { return config.getCassandraHost(); }
public int getReplicationFactor() { return config.getReplicationFactor(); }
public boolean isSchemaModificationEnabled() { return config.isSchemaModificationEnabled(); }
public Injector getInjector() { return injector; }
public DataDriver getDataDriver() { return dataDriver; }
public EntityManager getEntityManager() { return entityManager; }
public WideEntityManager getWideEntityManager() { return wideEntityManager; }
}