// Copyright 2017 JanusGraph Authors // // 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 org.janusgraph.diskstorage; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.janusgraph.core.JanusGraphConfigurationException; import org.janusgraph.core.JanusGraphException; import org.janusgraph.core.schema.JanusGraphManagement; import org.janusgraph.diskstorage.configuration.*; import org.janusgraph.diskstorage.idmanagement.ConsistentKeyIDAuthority; import org.janusgraph.diskstorage.indexing.*; import org.janusgraph.diskstorage.keycolumnvalue.*; import org.janusgraph.diskstorage.keycolumnvalue.cache.CacheTransaction; import org.janusgraph.diskstorage.keycolumnvalue.cache.ExpirationKCVSCache; import org.janusgraph.diskstorage.keycolumnvalue.cache.KCVSCache; import org.janusgraph.diskstorage.keycolumnvalue.cache.NoKCVSCache; import org.janusgraph.diskstorage.keycolumnvalue.keyvalue.*; import org.janusgraph.diskstorage.keycolumnvalue.scan.StandardScanner; import org.janusgraph.diskstorage.locking.Locker; import org.janusgraph.diskstorage.locking.LockerProvider; import org.janusgraph.diskstorage.locking.consistentkey.ConsistentKeyLocker; import org.janusgraph.diskstorage.locking.consistentkey.ExpectedValueCheckingStoreManager; import org.janusgraph.diskstorage.log.Log; import org.janusgraph.diskstorage.log.LogManager; import org.janusgraph.diskstorage.log.kcvs.KCVSLog; import org.janusgraph.diskstorage.log.kcvs.KCVSLogManager; import org.janusgraph.diskstorage.util.BackendOperation; import org.janusgraph.diskstorage.configuration.backend.KCVSConfiguration; import org.janusgraph.diskstorage.util.MetricInstrumentedStoreManager; import org.janusgraph.diskstorage.util.StandardBaseTransactionConfig; import org.janusgraph.diskstorage.util.time.TimestampProvider; import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import org.janusgraph.graphdb.transaction.TransactionConfiguration; import org.janusgraph.util.system.ConfigurationUtil; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.Nullable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.Duration; import java.util.*; import java.util.concurrent.*; import static org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration.*; /** * Orchestrates and configures all backend systems: * The primary backend storage ({@link KeyColumnValueStore}) and all external indexing providers ({@link IndexProvider}). * * @author Matthias Broecheler (me@matthiasb.com) */ public class Backend implements LockerProvider, AutoCloseable { private static final Logger log = LoggerFactory.getLogger(Backend.class); /** * These are the names for the edge store and property index databases, respectively. * The edge store contains all edges and properties. The property index contains an * inverted index from attribute value to vertex. * <p/> * These names are fixed and should NEVER be changed. Changing these strings can * disrupt storage adapters that rely on these names for specific configurations. */ public static final String EDGESTORE_NAME = "edgestore"; public static final String INDEXSTORE_NAME = "graphindex"; public static final String ID_STORE_NAME = "janusgraph_ids"; public static final String METRICS_STOREMANAGER_NAME = "storeManager"; public static final String METRICS_MERGED_STORE = "stores"; public static final String METRICS_MERGED_CACHE = "caches"; public static final String METRICS_CACHE_SUFFIX = ".cache"; public static final String LOCK_STORE_SUFFIX = "_lock_"; public static final String SYSTEM_TX_LOG_NAME = "txlog"; public static final String SYSTEM_MGMT_LOG_NAME = "systemlog"; public static final double EDGESTORE_CACHE_PERCENT = 0.8; public static final double INDEXSTORE_CACHE_PERCENT = 0.2; private static final long ETERNAL_CACHE_EXPIRATION = 1000l*3600*24*365*200; //200 years public static final int THREAD_POOL_SIZE_SCALE_FACTOR = 2; public static final Map<String, Integer> STATIC_KEY_LENGTHS = new HashMap<String, Integer>() {{ put(EDGESTORE_NAME, 8); put(EDGESTORE_NAME + LOCK_STORE_SUFFIX, 8); put(ID_STORE_NAME, 8); }}; private final KeyColumnValueStoreManager storeManager; private final KeyColumnValueStoreManager storeManagerLocking; private final StoreFeatures storeFeatures; private KCVSCache edgeStore; private KCVSCache indexStore; private KCVSCache txLogStore; private IDAuthority idAuthority; private KCVSConfiguration systemConfig; private KCVSConfiguration userConfig; private boolean hasAttemptedClose; private final StandardScanner scanner; private final KCVSLogManager mgmtLogManager; private final KCVSLogManager txLogManager; private final LogManager userLogManager; private final Map<String, IndexProvider> indexes; private final int bufferSize; private final Duration maxWriteTime; private final Duration maxReadTime; private final boolean cacheEnabled; private final ExecutorService threadPool; private final Function<String, Locker> lockerCreator; private final ConcurrentHashMap<String, Locker> lockers = new ConcurrentHashMap<String, Locker>(); private final Configuration configuration; public Backend(Configuration configuration) { this.configuration = configuration; KeyColumnValueStoreManager manager = getStorageManager(configuration); if (configuration.get(BASIC_METRICS)) { storeManager = new MetricInstrumentedStoreManager(manager,METRICS_STOREMANAGER_NAME,configuration.get(METRICS_MERGE_STORES),METRICS_MERGED_STORE); } else { storeManager = manager; } indexes = getIndexes(configuration); storeFeatures = storeManager.getFeatures(); mgmtLogManager = getKCVSLogManager(MANAGEMENT_LOG); txLogManager = getKCVSLogManager(TRANSACTION_LOG); userLogManager = getLogManager(USER_LOG); cacheEnabled = !configuration.get(STORAGE_BATCH) && configuration.get(DB_CACHE); int bufferSizeTmp = configuration.get(BUFFER_SIZE); Preconditions.checkArgument(bufferSizeTmp > 0, "Buffer size must be positive"); if (!storeFeatures.hasBatchMutation()) { bufferSize = Integer.MAX_VALUE; } else bufferSize = bufferSizeTmp; maxWriteTime = configuration.get(STORAGE_WRITE_WAITTIME); maxReadTime = configuration.get(STORAGE_READ_WAITTIME); if (!storeFeatures.hasLocking()) { Preconditions.checkArgument(storeFeatures.isKeyConsistent(),"Store needs to support some form of locking"); storeManagerLocking = new ExpectedValueCheckingStoreManager(storeManager,LOCK_STORE_SUFFIX,this,maxReadTime); } else { storeManagerLocking = storeManager; } if (configuration.get(PARALLEL_BACKEND_OPS)) { int poolsize = Runtime.getRuntime().availableProcessors() * THREAD_POOL_SIZE_SCALE_FACTOR; threadPool = Executors.newFixedThreadPool(poolsize); log.info("Initiated backend operations thread pool of size {}", poolsize); } else { threadPool = null; } final String lockBackendName = configuration.get(LOCK_BACKEND); if (REGISTERED_LOCKERS.containsKey(lockBackendName)) { lockerCreator = REGISTERED_LOCKERS.get(lockBackendName); } else { throw new JanusGraphConfigurationException("Unknown lock backend \"" + lockBackendName + "\". Known lock backends: " + Joiner.on(", ").join(REGISTERED_LOCKERS.keySet()) + "."); } // Never used for backends that have innate transaction support, but we // want to maintain the non-null invariant regardless; it will default // to connsistentkey impl if none is specified Preconditions.checkNotNull(lockerCreator); scanner = new StandardScanner(storeManager); } @Override public Locker getLocker(String lockerName) { Preconditions.checkNotNull(lockerName); Locker l = lockers.get(lockerName); if (null == l) { l = lockerCreator.apply(lockerName); final Locker x = lockers.putIfAbsent(lockerName, l); if (null != x) { l = x; } } return l; } /** * Initializes this backend with the given configuration. Must be called before this Backend can be used * * @param config */ public void initialize(Configuration config) { try { //EdgeStore & VertexIndexStore KeyColumnValueStore idStore = storeManager.openDatabase(ID_STORE_NAME); idAuthority = null; if (storeFeatures.isKeyConsistent()) { idAuthority = new ConsistentKeyIDAuthority(idStore, storeManager, config); } else { throw new IllegalStateException("Store needs to support consistent key or transactional operations for ID manager to guarantee proper id allocations"); } KeyColumnValueStore edgeStoreRaw = storeManagerLocking.openDatabase(EDGESTORE_NAME); KeyColumnValueStore indexStoreRaw = storeManagerLocking.openDatabase(INDEXSTORE_NAME); //Configure caches if (cacheEnabled) { long expirationTime = configuration.get(DB_CACHE_TIME); Preconditions.checkArgument(expirationTime>=0,"Invalid cache expiration time: %s",expirationTime); if (expirationTime==0) expirationTime=ETERNAL_CACHE_EXPIRATION; long cacheSizeBytes; double cachesize = configuration.get(DB_CACHE_SIZE); Preconditions.checkArgument(cachesize>0.0,"Invalid cache size specified: %s",cachesize); if (cachesize<1.0) { //Its a percentage Runtime runtime = Runtime.getRuntime(); cacheSizeBytes = (long)((runtime.maxMemory()-(runtime.totalMemory()-runtime.freeMemory())) * cachesize); } else { Preconditions.checkArgument(cachesize>1000,"Cache size is too small: %s",cachesize); cacheSizeBytes = (long)cachesize; } log.info("Configuring total store cache size: {}",cacheSizeBytes); long cleanWaitTime = configuration.get(DB_CACHE_CLEAN_WAIT); Preconditions.checkArgument(EDGESTORE_CACHE_PERCENT + INDEXSTORE_CACHE_PERCENT == 1.0,"Cache percentages don't add up!"); long edgeStoreCacheSize = Math.round(cacheSizeBytes * EDGESTORE_CACHE_PERCENT); long indexStoreCacheSize = Math.round(cacheSizeBytes * INDEXSTORE_CACHE_PERCENT); edgeStore = new ExpirationKCVSCache(edgeStoreRaw,getMetricsCacheName(EDGESTORE_NAME),expirationTime,cleanWaitTime,edgeStoreCacheSize); indexStore = new ExpirationKCVSCache(indexStoreRaw,getMetricsCacheName(INDEXSTORE_NAME),expirationTime,cleanWaitTime,indexStoreCacheSize); } else { edgeStore = new NoKCVSCache(edgeStoreRaw); indexStore = new NoKCVSCache(indexStoreRaw); } //Just open them so that they are cached txLogManager.openLog(SYSTEM_TX_LOG_NAME); mgmtLogManager.openLog(SYSTEM_MGMT_LOG_NAME); txLogStore = new NoKCVSCache(storeManager.openDatabase(SYSTEM_TX_LOG_NAME)); //Open global configuration KeyColumnValueStore systemConfigStore = storeManagerLocking.openDatabase(SYSTEM_PROPERTIES_STORE_NAME); systemConfig = getGlobalConfiguration(new BackendOperation.TransactionalProvider() { @Override public StoreTransaction openTx() throws BackendException { return storeManagerLocking.beginTransaction(StandardBaseTransactionConfig.of( configuration.get(TIMESTAMP_PROVIDER), storeFeatures.getKeyConsistentTxConfig())); } @Override public void close() throws BackendException { //Do nothing, storeManager is closed explicitly by Backend } },systemConfigStore,configuration); userConfig = getConfiguration(new BackendOperation.TransactionalProvider() { @Override public StoreTransaction openTx() throws BackendException { return storeManagerLocking.beginTransaction(StandardBaseTransactionConfig.of(configuration.get(TIMESTAMP_PROVIDER))); } @Override public void close() throws BackendException { //Do nothing, storeManager is closed explicitly by Backend } },systemConfigStore,USER_CONFIGURATION_IDENTIFIER,configuration); } catch (BackendException e) { throw new JanusGraphException("Could not initialize backend", e); } } /** * Get information about all registered {@link IndexProvider}s. * * @return */ public Map<String, IndexInformation> getIndexInformation() { ImmutableMap.Builder<String, IndexInformation> copy = ImmutableMap.builder(); copy.putAll(indexes); return copy.build(); } // // public IndexProvider getIndexProvider(String name) { // return indexes.get(name); // } public KCVSLog getSystemTxLog() { try { return txLogManager.openLog(SYSTEM_TX_LOG_NAME); } catch (BackendException e) { throw new JanusGraphException("Could not re-open transaction log", e); } } public Log getSystemMgmtLog() { try { return mgmtLogManager.openLog(SYSTEM_MGMT_LOG_NAME); } catch (BackendException e) { throw new JanusGraphException("Could not re-open management log", e); } } public StandardScanner.Builder buildEdgeScanJob() { return buildStoreIndexScanJob(EDGESTORE_NAME); } public StandardScanner.Builder buildGraphIndexScanJob() { return buildStoreIndexScanJob(INDEXSTORE_NAME); } private StandardScanner.Builder buildStoreIndexScanJob(String storeName) { TimestampProvider provider = configuration.get(TIMESTAMP_PROVIDER); ModifiableConfiguration jobConfig = GraphDatabaseConfiguration.buildJobConfiguration(); jobConfig.set(JOB_START_TIME,provider.getTime().toEpochMilli()); return scanner.build() .setStoreName(storeName) .setTimestampProvider(provider) .setJobConfiguration(jobConfig) .setGraphConfiguration(configuration) .setNumProcessingThreads(1) .setWorkBlockSize(10000); } public JanusGraphManagement.IndexJobFuture getScanJobStatus(Object jobId) { return scanner.getRunningJob(jobId); } public Log getUserLog(String identifier) throws BackendException { return userLogManager.openLog(getUserLogName(identifier)); } public static final String getUserLogName(String identifier) { Preconditions.checkArgument(StringUtils.isNotBlank(identifier)); return USER_LOG_PREFIX +identifier; } public KCVSConfiguration getGlobalSystemConfig() { return systemConfig; } public KCVSConfiguration getUserConfiguration() { return userConfig; } private String getMetricsCacheName(String storeName) { if (!configuration.get(BASIC_METRICS)) return null; return configuration.get(METRICS_MERGE_STORES) ? METRICS_MERGED_CACHE : storeName + METRICS_CACHE_SUFFIX; } public KCVSLogManager getKCVSLogManager(String logName) { Preconditions.checkArgument(configuration.restrictTo(logName).get(LOG_BACKEND).equalsIgnoreCase(LOG_BACKEND.getDefaultValue())); return (KCVSLogManager)getLogManager(logName); } public LogManager getLogManager(String logName) { return getLogManager(configuration, logName, storeManager); } private static LogManager getLogManager(Configuration config, String logName, KeyColumnValueStoreManager sm) { Configuration logConfig = config.restrictTo(logName); String backend = logConfig.get(LOG_BACKEND); if (backend.equalsIgnoreCase(LOG_BACKEND.getDefaultValue())) { return new KCVSLogManager(sm,logConfig); } else { Preconditions.checkArgument(config!=null); LogManager lm = getImplementationClass(logConfig,logConfig.get(LOG_BACKEND),REGISTERED_LOG_MANAGERS); Preconditions.checkNotNull(lm); return lm; } } public static KeyColumnValueStoreManager getStorageManager(Configuration storageConfig) { StoreManager manager = getImplementationClass(storageConfig, storageConfig.get(STORAGE_BACKEND), StandardStoreManager.getAllManagerClasses()); if (manager instanceof OrderedKeyValueStoreManager) { manager = new OrderedKeyValueStoreManagerAdapter((OrderedKeyValueStoreManager) manager, STATIC_KEY_LENGTHS); } Preconditions.checkArgument(manager instanceof KeyColumnValueStoreManager,"Invalid storage manager: %s",manager.getClass()); return (KeyColumnValueStoreManager) manager; } private static KCVSConfiguration getGlobalConfiguration(final BackendOperation.TransactionalProvider txProvider, final KeyColumnValueStore store, final Configuration config) { return getConfiguration(txProvider, store, SYSTEM_CONFIGURATION_IDENTIFIER, config); } private static KCVSConfiguration getConfiguration(final BackendOperation.TransactionalProvider txProvider, final KeyColumnValueStore store, final String identifier, final Configuration config) { try { KCVSConfiguration kcvsConfig = new KCVSConfiguration(txProvider,config,store,identifier); kcvsConfig.setMaxOperationWaitTime(config.get(SETUP_WAITTIME)); return kcvsConfig; } catch (BackendException e) { throw new JanusGraphException("Could not open global configuration",e); } } public static KCVSConfiguration getStandaloneGlobalConfiguration(final KeyColumnValueStoreManager manager, final Configuration config) { try { final StoreFeatures features = manager.getFeatures(); return getGlobalConfiguration(new BackendOperation.TransactionalProvider() { @Override public StoreTransaction openTx() throws BackendException { return manager.beginTransaction(StandardBaseTransactionConfig.of(config.get(TIMESTAMP_PROVIDER),features.getKeyConsistentTxConfig())); } @Override public void close() throws BackendException { manager.close(); } },manager.openDatabase(SYSTEM_PROPERTIES_STORE_NAME),config); } catch (BackendException e) { throw new JanusGraphException("Could not open global configuration",e); } } private final static Map<String, IndexProvider> getIndexes(Configuration config) { ImmutableMap.Builder<String, IndexProvider> builder = ImmutableMap.builder(); for (String index : config.getContainedNamespaces(INDEX_NS)) { Preconditions.checkArgument(StringUtils.isNotBlank(index), "Invalid index name [%s]", index); log.info("Configuring index [{}]", index); IndexProvider provider = getImplementationClass(config.restrictTo(index), config.get(INDEX_BACKEND,index), StandardIndexProvider.getAllProviderClasses()); Preconditions.checkNotNull(provider); builder.put(index, provider); } return builder.build(); } public final static <T> T getImplementationClass(Configuration config, String clazzname, Map<String, String> registeredImpls) { if (registeredImpls.containsKey(clazzname.toLowerCase())) { clazzname = registeredImpls.get(clazzname.toLowerCase()); } return ConfigurationUtil.instantiate(clazzname, new Object[]{config}, new Class[]{Configuration.class}); } /** * Returns the configured {@link IDAuthority}. * * @return */ public IDAuthority getIDAuthority() { Preconditions.checkNotNull(idAuthority, "Backend has not yet been initialized"); return idAuthority; } /** * Returns the {@link StoreFeatures} of the configured backend storage engine. * * @return */ public StoreFeatures getStoreFeatures() { return storeFeatures; } public Class<? extends KeyColumnValueStoreManager> getStoreManagerClass() { return storeManager.getClass(); } public StoreManager getStoreManager() { return storeManager; } /** * Returns the {@link IndexFeatures} of all configured index backends */ public Map<String,IndexFeatures> getIndexFeatures() { return Maps.transformValues(indexes,new Function<IndexProvider, IndexFeatures>() { @Nullable @Override public IndexFeatures apply(@Nullable IndexProvider indexProvider) { return indexProvider.getFeatures(); } }); } /** * Opens a new transaction against all registered backend system wrapped in one {@link BackendTransaction}. * * @return * @throws BackendException */ public BackendTransaction beginTransaction(TransactionConfiguration configuration, KeyInformation.Retriever indexKeyRetriever) throws BackendException { StoreTransaction tx = storeManagerLocking.beginTransaction(configuration); // Cache CacheTransaction cacheTx = new CacheTransaction(tx, storeManagerLocking, bufferSize, maxWriteTime, configuration.hasEnabledBatchLoading()); // Index transactions Map<String, IndexTransaction> indexTx = new HashMap<String, IndexTransaction>(indexes.size()); for (Map.Entry<String, IndexProvider> entry : indexes.entrySet()) { indexTx.put(entry.getKey(), new IndexTransaction(entry.getValue(), indexKeyRetriever.get(entry.getKey()), configuration, maxWriteTime)); } return new BackendTransaction(cacheTx, configuration, storeFeatures, edgeStore, indexStore, txLogStore, maxReadTime, indexTx, threadPool); } public synchronized void close() throws BackendException { if (!hasAttemptedClose) { hasAttemptedClose = true; mgmtLogManager.close(); txLogManager.close(); userLogManager.close(); scanner.close(); edgeStore.close(); indexStore.close(); idAuthority.close(); systemConfig.close(); userConfig.close(); storeManager.close(); if(threadPool != null) { threadPool.shutdown(); } //Indexes for (IndexProvider index : indexes.values()) index.close(); } else { log.debug("Backend {} has already been closed or cleared", this); } } /** * Clears the storage of all registered backend data providers. This includes backend storage engines and index providers. * <p/> * IMPORTANT: Clearing storage means that ALL data will be lost and cannot be recovered. * * @throws BackendException */ public synchronized void clearStorage() throws BackendException { if (!hasAttemptedClose) { hasAttemptedClose = true; mgmtLogManager.close(); txLogManager.close(); userLogManager.close(); scanner.close(); edgeStore.close(); indexStore.close(); idAuthority.close(); systemConfig.close(); userConfig.close(); storeManager.clearStorage(); storeManager.close(); //Indexes for (IndexProvider index : indexes.values()) { index.clearStorage(); index.close(); } } else { log.debug("Backend {} has already been closed or cleared", this); } } //############ Registered Storage Managers ############## private static final ImmutableMap<StandardStoreManager, ConfigOption<?>> STORE_SHORTHAND_OPTIONS; static { Map<StandardStoreManager, ConfigOption<?>> m = new HashMap<StandardStoreManager, ConfigOption<?>>(); m.put(StandardStoreManager.BDB_JE, STORAGE_DIRECTORY); m.put(StandardStoreManager.CASSANDRA_ASTYANAX, STORAGE_HOSTS); m.put(StandardStoreManager.CASSANDRA_EMBEDDED, STORAGE_CONF_FILE); m.put(StandardStoreManager.CASSANDRA_THRIFT, STORAGE_HOSTS); m.put(StandardStoreManager.HBASE, STORAGE_HOSTS); //m.put(StandardStorageBackend.IN_MEMORY, null); //STORE_SHORTHAND_OPTIONS = Maps.immutableEnumMap(m); STORE_SHORTHAND_OPTIONS = ImmutableMap.copyOf(m); } public static ConfigOption<?> getOptionForShorthand(String shorthand) { if (null == shorthand) return null; shorthand = shorthand.toLowerCase(); for (StandardStoreManager m : STORE_SHORTHAND_OPTIONS.keySet()) { if (m.getShorthands().contains(shorthand)) return STORE_SHORTHAND_OPTIONS.get(m); } return null; } public static final Map<String,String> REGISTERED_LOG_MANAGERS = new HashMap<String, String>() {{ put("default","org.janusgraph.diskstorage.log.kcvs.KCVSLogManager"); }}; private final Function<String, Locker> CONSISTENT_KEY_LOCKER_CREATOR = new Function<String, Locker>() { @Override public Locker apply(String lockerName) { KeyColumnValueStore lockerStore; try { lockerStore = storeManager.openDatabase(lockerName); } catch (BackendException e) { throw new JanusGraphConfigurationException("Could not retrieve store named " + lockerName + " for locker configuration", e); } return new ConsistentKeyLocker.Builder(lockerStore, storeManager).fromConfig(configuration).build(); } }; private final Function<String, Locker> ASTYANAX_RECIPE_LOCKER_CREATOR = new Function<String, Locker>() { @Override public Locker apply(String lockerName) { String expectedManagerName = "org.janusgraph.diskstorage.cassandra.astyanax.AstyanaxStoreManager"; String actualManagerName = storeManager.getClass().getCanonicalName(); // Require AstyanaxStoreManager Preconditions.checkArgument(expectedManagerName.equals(actualManagerName), "Astyanax Recipe locker is only supported with the Astyanax storage backend (configured:" + actualManagerName + " != required:" + expectedManagerName + ")"); try { Class<?> c = storeManager.getClass(); Method method = c.getMethod("openLocker", String.class); Object o = method.invoke(storeManager, lockerName); return (Locker) o; } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Could not find method when configuring locking with Astyanax Recipes"); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Could not access method when configuring locking with Astyanax Recipes", e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("Could not invoke method when configuring locking with Astyanax Recipes", e); } } }; private final Function<String, Locker> TEST_LOCKER_CREATOR = new Function<String, Locker>() { @Override public Locker apply(String lockerName) { return openManagedLocker("org.janusgraph.diskstorage.util.TestLockerManager",lockerName); } }; private final Map<String, Function<String, Locker>> REGISTERED_LOCKERS = ImmutableMap.of( "consistentkey", CONSISTENT_KEY_LOCKER_CREATOR, "astyanaxrecipe", ASTYANAX_RECIPE_LOCKER_CREATOR, "test", TEST_LOCKER_CREATOR ); private static Locker openManagedLocker(String classname, String lockerName) { try { Class c = Class.forName(classname); Constructor constructor = c.getConstructor(); Object instance = constructor.newInstance(); Method method = c.getMethod("openLocker", String.class); Object o = method.invoke(instance, lockerName); return (Locker) o; } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Could not find implementation class: " + classname); } catch (InstantiationException e) { throw new IllegalArgumentException("Could not instantiate implementation: " + classname, e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException("Could not find method when configuring locking for: " + classname,e); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Could not access method when configuring locking for: " + classname,e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("Could not invoke method when configuring locking for: " + classname,e); } catch (ClassCastException e) { throw new IllegalArgumentException("Could not instantiate implementation: " + classname, e); } } }