package me.prettyprint.hom;
import me.prettyprint.cassandra.model.ConfigurableConsistencyLevel;
import me.prettyprint.cassandra.service.CassandraHostConfigurator;
import me.prettyprint.cassandra.service.ThriftCluster;
import me.prettyprint.hector.api.HConsistencyLevel;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.factory.HFactory;
import me.prettyprint.hom.annotations.AnnotationScanner;
import me.prettyprint.hom.annotations.DefaultAnnotationScanner;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SharedCacheMode;
import javax.persistence.ValidationMode;
import javax.persistence.spi.ClassTransformer;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import javax.persistence.spi.ProviderUtil;
import javax.sql.DataSource;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import static me.prettyprint.hom.EntityManagerFactoryImpl.PACKAGES_TO_SCAN;
public class CassandraPersistenceProvider implements PersistenceProvider {
private static final String KEYSPACE_PROP = "me.prettyprint.keyspace";
private static final String CLUSTER_PROP = "me.prettyprint.cluster";
private static final String HOST_PROP = "me.prettyprint.host";
private static final String CONSISTENCY_PROP = "me.prettyprint.consistency";
private static final String SCANNER = "me.prettyprint.scanner";
@Override
public EntityManagerFactory createEntityManagerFactory(final String emName, final Map map) {
final Keyspace keyspace = keyspace((String) map.get(HOST_PROP), (String) map.get(CLUSTER_PROP), (String) map.get(KEYSPACE_PROP), (String) map.get(CONSISTENCY_PROP));
final AnnotationScanner scanner = createScanner(value(SCANNER, map, null));
return new EntityManagerFactoryImpl(new LightPersistenceUnitInfo((String) map.get(PACKAGES_TO_SCAN)), keyspace, scanner);
}
@Override
public EntityManagerFactory createContainerEntityManagerFactory(final PersistenceUnitInfo info, final Map map) {
final String keyspaceName = value(KEYSPACE_PROP, map, info.getProperties());
final String host = value(HOST_PROP, map, info.getProperties());
final String clusterName = value(CLUSTER_PROP, map, info.getProperties());
final String consistency = value(CONSISTENCY_PROP, map, info.getProperties());
final Keyspace keyspace = keyspace(host, clusterName, keyspaceName, consistency);
final AnnotationScanner scanner = createScanner(value(SCANNER, map, info.getProperties()));
return new EntityManagerFactoryImpl(info, keyspace, scanner);
}
private static AnnotationScanner createScanner(final String classname) {
if (classname == null) {
return new DefaultAnnotationScanner();
}
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = CassandraPersistenceProvider.class.getClassLoader();
}
AnnotationScanner scanner;
try {
scanner = (AnnotationScanner) loader.loadClass(classname).newInstance();
} catch (Exception e) {
scanner = new DefaultAnnotationScanner();
}
return scanner;
}
private static String value(final String key, final Map map, final Properties properties) {
if (map != null && map.containsKey(key)) {
return (String) map.get(key);
}
if (properties != null) {
return properties.getProperty(key);
}
return null;
}
private Keyspace keyspace(final String host, final String clusterName, final String keyspaceName, final String consistency) {
if (clusterName == null) {
throw new IllegalArgumentException("cluster name can't be null");
}
if (keyspaceName == null) {
throw new IllegalArgumentException("keyspace name can't be null");
}
if (host == null) {
throw new IllegalArgumentException("host can't be null");
}
final ConfigurableConsistencyLevel consistencyLevelPolicy = new ConfigurableConsistencyLevel();
if (consistency == null) {
consistencyLevelPolicy.setDefaultReadConsistencyLevel(HConsistencyLevel.QUORUM);
} else {
consistencyLevelPolicy.setDefaultReadConsistencyLevel(HConsistencyLevel.valueOf(consistency.toUpperCase()));
}
final ThriftCluster cluster = new ThriftCluster(clusterName, new CassandraHostConfigurator(host));
return HFactory.createKeyspace(keyspaceName, cluster, consistencyLevelPolicy);
}
@Override
public ProviderUtil getProviderUtil() {
return null; // null == JPA 1.0, that's fine for a lot of cases
}
// just for the JSE mode, a mock impl implementing what we use
private class LightPersistenceUnitInfo implements PersistenceUnitInfo {
private String toScan;
private LightPersistenceUnitInfo(String toScan) {
this.toScan = toScan;
}
@Override
public String getPersistenceUnitName() {
return null;
}
@Override
public String getPersistenceProviderClassName() {
return CassandraPersistenceProvider.class.getName();
}
@Override
public PersistenceUnitTransactionType getTransactionType() {
return null;
}
@Override
public DataSource getJtaDataSource() {
return null;
}
@Override
public DataSource getNonJtaDataSource() {
return null;
}
@Override
public List<String> getMappingFileNames() {
return null;
}
@Override
public List<URL> getJarFileUrls() {
return null;
}
@Override
public URL getPersistenceUnitRootUrl() {
return null;
}
@Override
public List<String> getManagedClassNames() {
return null;
}
@Override
public boolean excludeUnlistedClasses() {
return true;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return null;
}
@Override
public ValidationMode getValidationMode() {
return null;
}
@Override
public Properties getProperties() {
final Properties properties = new Properties();
properties.setProperty(PACKAGES_TO_SCAN, toScan);
return properties;
}
@Override
public String getPersistenceXMLSchemaVersion() {
return null;
}
@Override
public ClassLoader getClassLoader() {
return null;
}
@Override
public void addTransformer(ClassTransformer transformer) {
}
@Override
public ClassLoader getNewTempClassLoader() {
return null;
}
}
}