info.archinnov.achilles.persistence.PersistenceManagerFactory Maven / Gradle / Ivy
Show all versions of achilles-core Show documentation
/*
* Copyright (C) 2012-2014 DuyHai DOAN
*
* 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 info.archinnov.achilles.persistence;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
import com.fasterxml.jackson.databind.ObjectMapper;
import info.archinnov.achilles.configuration.ArgumentExtractor;
import info.archinnov.achilles.configuration.ConfigurationParameters;
import info.archinnov.achilles.interceptor.Interceptor;
import info.archinnov.achilles.internal.context.ConfigurationContext;
import info.archinnov.achilles.internal.context.DaoContext;
import info.archinnov.achilles.internal.context.PersistenceContextFactory;
import info.archinnov.achilles.internal.context.SchemaContext;
import info.archinnov.achilles.internal.metadata.discovery.AchillesBootstrapper;
import info.archinnov.achilles.internal.metadata.holder.EntityMeta;
import info.archinnov.achilles.internal.metadata.parsing.context.ParsingResult;
import info.archinnov.achilles.internal.proxy.ProxyClassFactory;
import info.archinnov.achilles.internal.utils.ConfigMap;
import info.archinnov.achilles.internal.validation.Validator;
import info.archinnov.achilles.json.JacksonMapperFactory;
import info.archinnov.achilles.type.ConsistencyLevel;
import info.archinnov.achilles.type.InsertStrategy;
import javax.annotation.PreDestroy;
import static com.datastax.driver.core.BatchStatement.Type.LOGGED;
import static com.datastax.driver.core.BatchStatement.Type.UNLOGGED;
import static info.archinnov.achilles.configuration.ConfigurationParameters.*;
/**
*
* Stateless factory to create persistence manager.
* This class is totally thread-safe and can be shared by many threads.
* You should normally have only one instance of PersistenceManagerFactory across the application
*
*/
public class PersistenceManagerFactory {
private static final Logger log = LoggerFactory.getLogger(PersistenceManagerFactory.class);
Map, EntityMeta> entityMetaMap = new HashMap<>();
ConfigurationContext configContext;
DaoContext daoContext;
PersistenceContextFactory contextFactory;
ConfigMap configurationMap;
private ArgumentExtractor argumentExtractor = new ArgumentExtractor();
private AchillesBootstrapper bootstrapper = new AchillesBootstrapper();
private ProxyClassFactory proxyClassFactory = ProxyClassFactory.Singleton.INSTANCE.get();
private Cluster cluster;
PersistenceManagerFactory(Cluster cluster, Map configurationMap) {
this.cluster = cluster;
Validator.validateNotNull(configurationMap, "Configuration map for PersistenceManagerFactory should not be null");
Validator.validateNotEmpty(configurationMap, "Configuration map for PersistenceManagerFactory should not be empty");
this.configurationMap = ConfigMap.fromMap(configurationMap);
}
PersistenceManagerFactory bootstrap() {
final String keyspaceName = configurationMap.getTyped(KEYSPACE_NAME);
log.info("Bootstrapping Achilles PersistenceManagerFactory for keyspace {}", keyspaceName);
configContext = argumentExtractor.initConfigContext(configurationMap);
Session session = argumentExtractor.initSession(cluster, configurationMap);
final ClassLoader classLoader = argumentExtractor.initOSGIClassLoader(configurationMap);
List> interceptors = argumentExtractor.initInterceptors(configurationMap);
List> candidateClasses = argumentExtractor.initEntities(configurationMap, classLoader);
ParsingResult parsingResult = parseEntities(candidateClasses);
this.entityMetaMap = parsingResult.getMetaMap();
bootstrapper.addInterceptorsToEntityMetas(interceptors, parsingResult.getMetaMap());
SchemaContext schemaContext = new SchemaContext(configContext, session, keyspaceName, cluster, parsingResult);
bootstrapper.validateOrCreateTables(schemaContext);
daoContext = bootstrapper.buildDaoContext(session, parsingResult, configContext);
contextFactory = new PersistenceContextFactory(daoContext, configContext, parsingResult.getMetaMap());
warmUpProxies();
return this;
}
private void warmUpProxies() {
if (argumentExtractor.initProxyWarmUp(configurationMap)) {
long start = System.nanoTime();
for (Class> clazz : entityMetaMap.keySet()) {
proxyClassFactory.createProxyClass(clazz, configContext);
}
long end = System.nanoTime();
long duration = (end - start) / 1000000;
log.info("Entity proxies warm up took {} milli secs for {} entities", duration, entityMetaMap.size());
}
}
private ParsingResult parseEntities(List> candidateClasses) {
return bootstrapper.buildMetaDatas(configContext, candidateClasses);
}
/**
* Create a new PersistenceManager. This instance of PersistenceManager is
* thread-safe
*
* @return PersistenceManager
*/
public PersistenceManager createPersistenceManager() {
log.debug("Spawn new PersistenceManager");
return new PersistenceManager(entityMetaMap, contextFactory, daoContext, configContext);
}
/**
* Create a new AsyncManager for asynchronous operation. This instance of PersistenceManager is
* thread-safe
*
* @return AsyncManager
*/
public AsyncManager createAsyncManager() {
log.debug("Spawn new AsyncManager");
return new AsyncManager(entityMetaMap, contextFactory, daoContext, configContext);
}
/**
* Create a new state-full LOGGED Batch
*
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full Batch
*/
public Batch createLoggedBatch() {
log.debug("Spawn new Logged Batch");
return new Batch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, false);
}
/**
* Create a new state-full UNLOGGED Batch
*
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full Batch
*/
public Batch createUnloggedBatch() {
log.debug("Spawn new Unlogged Batch");
return new Batch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, false);
}
/**
* Create a new asynchronous state-full LOGGED Batch
*
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full AsyncBatch
*/
public AsyncBatch createAsyncLoggedBatch() {
log.debug("Spawn new Logged AsyncBatch");
return new AsyncBatch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, false);
}
/**
* Create a new asynchronous state-full UNLOGGED Batch
*
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full AsyncBatch
*/
public AsyncBatch createAsyncUnloggedBatch() {
log.debug("Spawn new Unlogged AsyncBatch");
return new AsyncBatch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, false);
}
/**
* Create a new state-full ordered and LOGGED Batch
*
*
* This Batch respect insertion order by generating increasing timestamp with micro second resolution.
* If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients
* to avoid statements interleaving
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full Batch
*/
public Batch createOrderedLoggedBatch() {
log.debug("Spawn new ordered Logged Batch");
return new Batch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, true);
}
/**
* Create a new state-full ordered and UNLOGGED Batch
*
*
* This Batch respect insertion order by generating increasing timestamp with micro second resolution.
* If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients
* to avoid statements interleaving
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full Batch
*/
public Batch createOrderedUnloggedBatch() {
log.debug("Spawn new ordered Unlogged Batch");
return new Batch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, true);
}
/**
* Create a new state-full ordered and LOGGED Batch
*
*
* This Batch respect insertion order by generating increasing timestamp with micro second resolution.
* If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients
* to avoid statements interleaving
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full AsyncBatch
*/
public AsyncBatch createOrderedAsyncLoggedBatch() {
log.debug("Spawn new ordered Logged AsyncBatch");
return new AsyncBatch(entityMetaMap, contextFactory, daoContext, configContext, LOGGED, true);
}
/**
* Create a new state-full ordered and UNLOGGED Batch
*
*
* This Batch respect insertion order by generating increasing timestamp with micro second resolution.
* If you use ordered Batch in multiple clients, do not forget to synchronize the clock between those clients
* to avoid statements interleaving
*
* WARNING : This Batch is state-full and not
* thread-safe. In case of exception, you MUST not re-use it but create
* another one
*
* @return a new state-full AsyncBatch
*/
public AsyncBatch createOrderedAsyncUnloggedBatch() {
log.debug("Spawn new ordered Unlogged AsyncBatch");
return new AsyncBatch(entityMetaMap, contextFactory, daoContext, configContext, UNLOGGED, true);
}
/**
* Serialize the entity in JSON using a registered Object Mapper or default Achilles Object Mapper
* @param entity
* @return serialized entity in JSON
* @throws IOException
*/
public String serializeToJSON(Object entity) throws IOException {
Validator.validateNotNull(entity, "Cannot serialize to JSON null entity");
final ObjectMapper objectMapper = configContext.getMapperFor(entity.getClass());
return objectMapper.writeValueAsString(entity);
}
/**
* Deserialize the given JSON into entity using a registered Object Mapper or default Achilles Object Mapper
* @param type
* @param serialized
* @param
* @return deserialized entity from JSON
* @throws IOException
*/
public T deserializeFromJSON(Class type, String serialized) throws IOException {
Validator.validateNotNull(type, "Cannot deserialize from JSON if target type is null");
final ObjectMapper objectMapper = configContext.getMapperFor(type);
return objectMapper.readValue(serialized, type);
}
public static class PersistenceManagerFactoryBuilder {
private ConfigMap configMap = new ConfigMap();
private Cluster cluster;
private PersistenceManagerFactoryBuilder(Cluster cluster) {
this.cluster = cluster;
Validator.validateNotNull(cluster, "Cluster object should not be null");
}
/**
* Create a new PersistenceManagerFactory with the given configuration
* map
*
* @see Configuration parameters
*
* @param cluster pre-configured {@link com.datastax.driver.core.Cluster}
* @param configurationMap configuration map
* @return new PersistenceManagerFactory
*
*/
public static PersistenceManagerFactory build(Cluster cluster, Map configurationMap) {
return new PersistenceManagerFactory(cluster, configurationMap).bootstrap();
}
/**
* Create a new builder to configure each parameter
*
* @param cluster pre-configured {@link com.datastax.driver.core.Cluster}
* @return PersistenceManagerFactoryBuilder
*/
public static PersistenceManagerFactoryBuilder builder(Cluster cluster) {
return new PersistenceManagerFactoryBuilder(cluster);
}
/**
* Define entity packages to scan for '@Entity' classes The packages
* should be comma-separated
*
* @see Entity parsing
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withEntityPackages(String entityPackages) {
configMap.put(ENTITY_PACKAGES, entityPackages);
return this;
}
/**
* Define list of entities to be managed by Achilles
*
* @see Entity parsing
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withEntities(List> entities) {
configMap.put(ENTITIES_LIST, entities);
return this;
}
/**
* Define a pre-configured Jackson Object Mapper for serialization of
* non-primitive types
*
* @see JSON serialization
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withJacksonMapper(ObjectMapper objectMapper) {
configMap.put(JACKSON_MAPPER, objectMapper);
return this;
}
/**
* Define a pre-configured map of Jackson Object Mapper for
* serialization of non-primitive types
*
* @see JSON serialization
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withJacksonMapperFactory(JacksonMapperFactory jacksonMapperFactory) {
configMap.put(JACKSON_MAPPER_FACTORY, jacksonMapperFactory);
return this;
}
/**
* Define the default Consistency level to be used for all READ
* operations
*
* @see Consistency configuration
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withDefaultReadConsistency(ConsistencyLevel defaultReadConsistency) {
configMap.put(CONSISTENCY_LEVEL_READ_DEFAULT, defaultReadConsistency);
return this;
}
/**
* Define the default Consistency level to be used for all WRITE
* operations
*
* @see Consistency configuration
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withDefaultWriteConsistency(ConsistencyLevel defaultWriteConsistency) {
configMap.put(CONSISTENCY_LEVEL_WRITE_DEFAULT, defaultWriteConsistency);
return this;
}
/**
* Define the default Consistency level map to be used for all READ
* operations The map keys represent table names and values represent
* the corresponding consistency level
*
* @see Consistency configuration
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withDefaultReadConsistencyMap(Map readConsistencyMap) {
configMap.put(CONSISTENCY_LEVEL_READ_MAP, readConsistencyMap);
return this;
}
/**
* Define the default Consistency level map to be used for all WRITE
* operations The map keys represent table names and values represent
* the corresponding consistency level
*
* @see Consistency configuration
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withDefaultWriteConsistencyMap(Map writeConsistencyMap) {
configMap.put(CONSISTENCY_LEVEL_WRITE_MAP, writeConsistencyMap);
return this;
}
/**
* Whether Achilles should force table creation if they do not already
* exist in the keyspace This flag is useful for dev only. It
* should be disabled in production
*
* @see Table generation
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder forceTableCreation(boolean forceTableCreation) {
configMap.put(FORCE_TABLE_CREATION, forceTableCreation);
return this;
}
/**
* Whether Achilles should force update table if entities have new fields and table not.
* This flag is useful for dev only. It is strongly advised to disable this feature in production
*
* @see Lossless Schema Updae
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder enableSchemaUpdate(boolean forceTableUpdate) {
configMap.put(ENABLE_SCHEMA_UPDATE, forceTableUpdate);
return this;
}
/**
* Map to allow table schema update, same as
* {@link #enableSchemaUpdate(boolean)} per table. It is strongly advised to disable this feature in production
*
* @see Lossless Schema Update
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder enableSchemaUpdateForTables(Map tables) {
configMap.put(ENABLE_SCHEMA_UPDATE_FOR_TABLES, tables);
return this;
}
/**
* Define the pre-configured {@code com.datastax.driver.core.Session} object to
* be used instead of creating a new one
*
* @see Native Session
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withNativeSession(Session nativeSession) {
configMap.put(NATIVE_SESSION, nativeSession);
return this;
}
/**
* Define the keyspace name to be used by Achilles. Note: you should
* build as many PersistenceManagerFactory as different keyspaces to be
* used
*
* @see Keyspace
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withKeyspaceName(String keyspaceName) {
configMap.put(KEYSPACE_NAME, keyspaceName);
return this;
}
/**
* Provide a list of event interceptors
*
* @see Event interceptors
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withEventInterceptors(List> interceptors) {
configMap.put(EVENT_INTERCEPTORS, interceptors);
return this;
}
/**
* Activate Bean Validation (JSR303)
*
* @see Bean validation
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder enableBeanValidation(boolean enableBeanValidation) {
configMap.put(BEAN_VALIDATION_ENABLE, enableBeanValidation);
return this;
}
/**
* Provide custom validator for Bean Validation (JSR303)
*
* @see Bean validation
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withBeanValidator(javax.validation.Validator validator) {
if (validator != null) {
configMap.put(BEAN_VALIDATION_VALIDATOR, validator);
}
return this;
}
/**
* Specify maximum size for the internal prepared statements LRU cache.
* If the cache is full, oldest prepared statements will be dropped, leading to unexpected behavior.
*
* Default value is 5000, which is a pretty safe limit.
*
* For information, only selects on counter fields and updates are put into the cache because they cannot be
* prepared before hand since the updated properties are not known in advance.
*
* @see Prepared statements cache
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withMaxPreparedStatementCacheSize(int maxPreparedStatementCacheSize) {
configMap.put(PREPARED_STATEMENTS_CACHE_SIZE, maxPreparedStatementCacheSize);
return this;
}
/**
* Whether to disable proxies warm up or not.
*
* @see Proxies Warm Up
* @param disableProxiesWarmUp
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder disableProxiesWarmUp(boolean disableProxiesWarmUp) {
configMap.put(PROXIES_WARM_UP_DISABLED, disableProxiesWarmUp);
return this;
}
/**
* Define the global insert strategy
*
* @see Insert Strategy
* @param globalInsertStrategy
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder globalInsertStrategy(InsertStrategy globalInsertStrategy) {
configMap.put(GLOBAL_INSERT_STRATEGY, globalInsertStrategy);
return this;
}
/**
* Whether to relax constraint on existing secondary indices validation
*
* @see Index Validation
* @param relaxIndexValidation
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder relaxIndexValidation(boolean relaxIndexValidation) {
configMap.put(RELAX_INDEX_VALIDATION, relaxIndexValidation);
return this;
}
/**
* Pass an ExecutorService (ThreadPool) to Achilles to be used internally for asynchronous operations.
*
* If omitted, the default ExecutorService is configured as below:
*
* new ThreadPoolExecutor(5, 20, 60, TimeUnit.SECONDS,
* new LinkedBlockingQueue(1000),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param executorService an executor service (thread pool) to be used by Achilles for internal for asynchronous operations
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withDefaultExecutorService(ExecutorService executorService) {
configMap.put(EXECUTOR_SERVICE, executorService);
return this;
}
/**
* Define the min thread count for the ExecutorService (ThreadPool) to be used internally for asynchronous operations.
*
* The default ExecutorService is configured as below:
*
* // Create proxy
* new ThreadPoolExecutor(minThreadCount, 20, 60, TimeUnit.SECONDS,
* new LinkedBlockingQueue(1000),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param minThreadCount min thread count for the executor service
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withExecutorServiceMinThreadCount(int minThreadCount) {
configMap.put(DEFAULT_EXECUTOR_SERVICE_MIN_THREAD, minThreadCount);
return this;
}
/**
* Define the max thread count for the ExecutorService (ThreadPool) to be used internally for asynchronous operations.
*
* The default ExecutorService is configured as below:
*
* // Create proxy
* new ThreadPoolExecutor(5, maxThreadCount, 60, TimeUnit.SECONDS,
* new LinkedBlockingQueue(1000),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param maxThreadCount max thread count for the executor service
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withExecutorServiceMaxThreadCount(int maxThreadCount) {
configMap.put(DEFAULT_EXECUTOR_SERVICE_MAX_THREAD, maxThreadCount);
return this;
}
/**
* Define the thread keep-alive duration in second for the ExecutorService (ThreadPool) to be used internally for asynchronous operations.
*
* The default ExecutorService is configured as below:
*
* // Create proxy
* new ThreadPoolExecutor(5, 20, keepAliveDuration, TimeUnit.SECONDS,
* new LinkedBlockingQueue(1000),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param keepAliveDuration thread keep-alive duration in second for the executor service
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withExecutorServiceThreadKeepAliveDuration(int keepAliveDuration) {
configMap.put(DEFAULT_EXECUTOR_SERVICE_THREAD_KEEPALIVE, keepAliveDuration);
return this;
}
/**
* Define the LinkedBlockingQueue size for the ExecutorService (ThreadPool) to be used internally for asynchronous operations.
*
* The default ExecutorService is configured as below:
*
* // Create proxy
* new ThreadPoolExecutor(5, 20, 60, TimeUnit.SECONDS,
* new LinkedBlockingQueue(threadQueueSize),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param threadQueueSize the LinkedBlockingQueue size for the executor service
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withExecutorServiceThreadQueueSize(int threadQueueSize) {
configMap.put(DEFAULT_EXECUTOR_SERVICE_QUEUE_SIZE, threadQueueSize);
return this;
}
/**
* Define the Thread Factory for the ExecutorService (ThreadPool) to be used internally for asynchronous operations.
*
* The default ExecutorService is configured as below:
*
* // Create proxy
* new ThreadPoolExecutor(5, 20, 60, TimeUnit.SECONDS,
* new LinkedBlockingQueue(threadQueueSize),
* new DefaultExecutorThreadFactory())
*
* @see Asynchronous Operations
* @param factory the thread factory
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withExecutorServiceThreadFactory(ThreadFactory factory) {
configMap.put(DEFAULT_EXECUTOR_SERVICE_THREAD_FACTORY, factory);
return this;
}
/**
* Pass an arbitrary parameter to configure Achilles
*
* @param parameter an instance of the ConfigurationParameters enum
* @param value the value of the parameter
*
* @return PersistenceManagerFactoryBuilder
*/
public PersistenceManagerFactoryBuilder withParameter(ConfigurationParameters parameter, Object value) {
configMap.put(parameter, value);
return this;
}
/**
* Build a new PersistenceManagerFactory
*
* @return PersistenceManagerFactory
*/
public PersistenceManagerFactory build() {
return new PersistenceManagerFactory(cluster, configMap).bootstrap();
}
}
/**
* Call shutdown on Achilles, especially shutdown the internal thread pool handling asynchronous tasks
*/
@PreDestroy
public void shutDown() {
if(this.configurationMap.getTyped(EXECUTOR_SERVICE) == null) {
this.configContext.getExecutorService().shutdown();
}
}
@Override
public String toString() {
return "PersistenceManagerFactory for keyspace : "+this.configContext.getCurrentKeyspace().or("none");
}
}