package com.thinkaurelius.titan.graphdb.configuration;
import com.thinkaurelius.titan.diskstorage.keycolumnvalue.StoreFeatures;
import com.thinkaurelius.titan.graphdb.database.cache.ExpirationStoreCache;
import com.thinkaurelius.titan.graphdb.database.cache.PassThroughStoreCache;
import com.thinkaurelius.titan.graphdb.database.cache.StoreCache;
import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import javax.management.MBeanServerFactory;
import org.apache.commons.configuration.BaseConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.tools.ant.types.Assertions.EnabledAssertion;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.thinkaurelius.titan.core.AttributeHandler;
import com.thinkaurelius.titan.core.DefaultTypeMaker;
import com.thinkaurelius.titan.diskstorage.Backend;
import com.thinkaurelius.titan.graphdb.blueprints.BlueprintsDefaultTypeMaker;
import com.thinkaurelius.titan.graphdb.database.idassigner.VertexIDAssigner;
import com.thinkaurelius.titan.graphdb.database.serialize.Serializer;
import com.thinkaurelius.titan.graphdb.database.serialize.kryo.KryoSerializer;
import com.thinkaurelius.titan.graphdb.transaction.StandardTransactionBuilder;
import com.thinkaurelius.titan.graphdb.types.DisableDefaultTypeMaker;
import com.thinkaurelius.titan.util.stats.MetricManager;
/**
* Provides functionality to configure a {@link com.thinkaurelius.titan.core.TitanGraph} INSTANCE.
* <p/>
* <p/>
* A graph database configuration is uniquely associated with a graph database and must not be used for multiple
* databases.
* <p/>
* After a graph database has been initialized with respect to a configuration, some parameters of graph database
* configuration may no longer be modifiable.
*
* @author Matthias Bröcheler (me@matthiasb.com);
*/
public class GraphDatabaseConfiguration {
private static final Logger log =
LoggerFactory.getLogger(GraphDatabaseConfiguration.class);
// ################ GENERAL #######################
// ################################################
/**
* Configures the {@link DefaultTypeMaker} to be used by this graph. If left empty, automatic creation of types
* is disabled.
*/
public static final String AUTO_TYPE_KEY = "autotype";
public static final String AUTO_TYPE_DEFAULT = "blueprints";
private static final Map<String, DefaultTypeMaker> preregisteredAutoType = new HashMap<String, DefaultTypeMaker>() {{
put("none", DisableDefaultTypeMaker.INSTANCE);
put("blueprints", BlueprintsDefaultTypeMaker.INSTANCE);
}};
/**
* If this option is enabled, a transaction will retrieval all of a vertex's properties when asking for any property.
* This will significantly speed up subsequent property lookups on the same vertex, hence this option is enabled by default.
* Disable this option when the graph contains vertices with very many properties such that retrieving all of them substantially
* increases latencies compared to a single property retrieval.
*/
public static final String PROPERTY_PREFETCHING_KEY = "fast-property";
/**
* When enabled, Titan will accept user provided vertex ids as long as they are valid Titan vertex ids - see
* {@link com.thinkaurelius.titan.core.util.TitanId#toVertexId(long)}. When enabled, Titan will now longer allocate and assign
* ids internally, so all vertices must be added through {@link com.thinkaurelius.titan.core.TitanTransaction#addVertex(Long)}.
* <p/>
* Use this setting WITH GREAT CARE since it can easily lead to data corruption and performance issues when not used correctly.
* This should only ever be used when mapping external to internal ids causes performance issues at very large scale.
*/
public static final String ALLOW_SETTING_VERTEX_ID_KEY = "set-vertex-id";
public static final boolean ALLOW_SETTING_VERTEX_ID_DEFAULT = false;
public static final String IGNORE_UNKNOWN_INDEX_FIELD_KEY = "ignore-unknown-index-key";
public static final boolean IGNORE_UNKNOWN_INDEX_FIELD_DEFAULT = false;
public static final String UKNOWN_FIELD_NAME = "unknown_key";
// ################ CACHE #######################
// ################################################
public static final String CACHE_NAMESPACE = "cache";
/**
* Whether this Titan instance should use a database level cache in front of the
* storage backend in order to speed up frequent queries across transactions
*/
public static final String DB_CACHE_KEY = "db-cache";
public static final boolean DB_CACHE_DEFAULT = false;
/**
* The size of the database level cache.
* If this value is between 0.0 (strictly bigger) and 1.0 (strictly smaller), then it is interpreted as a
* percentage of the total heap space available to the JVM this Titan instance is running in.
* If this value is bigger than 1.0 it is interpreted as an absolute size in bytes.
*/
public static final String DB_CACHE_SIZE_KEY = "db-cache-size";
public static final double DB_CACHE_SIZE_DEFAULT = 0.3;
/**
* How long the database level cache will keep keys expired while the mutations that triggered the expiration
* are being persisted. This value should be larger than the time it takes for persisted mutations to become visible.
* This setting only ever makes sense for distributed storage backends where writes may be accepted but are not
* immediately readable.
*/
public static final String DB_CACHE_CLEAN_WAIT_KEY = "db-cache-clean-wait";
public static final long DB_CACHE_CLEAN_WAIT_DEFAULT = 50;
/**
* The default expiration time for elements held in the database level cache. This is the time period before
* Titan will check against storage backend for a newer query answer.
* Setting this value to 0 will cache elements forever (unless they get evicted due to space constraints). This only
* makes sense when this is the only Titan instance interacting with a storage backend.
*/
public static final String DB_CACHE_TIME_KEY = "db-cache-time";
public static final long DB_CACHE_TIME_DEFAULT = 10000;
private static final long ETERNAL_CACHE_EXPIRATION = 1000l*3600*24*365*200; //200 years
/**
* Configures the cache size used by individual transactions opened against this graph. The smaller the cache size, the
* less memory a transaction can consume at maximum. For many concurrent, long running transactions in memory constraint
* environments, reducing the cache size can avoid OutOfMemory and GC limit exceeded exceptions.
* Note, however, that all modifications in a transaction must always be kept in memory and hence this setting does not
* have much impact on write intense transactions. Those must be split into smaller transactions in the case of memory errors.
*/
public static final String TX_CACHE_SIZE_KEY = "tx-cache-size";
public static final int TX_CACHE_SIZE_DEFAULT = 20000;
// ################ STORAGE #######################
// ################################################
public static final String STORAGE_NAMESPACE = "storage";
/**
* Storage directory for those storage backends that require local storage
*/
public static final String STORAGE_DIRECTORY_KEY = "directory";
/**
* Path to a configuration file for those storage backends that
* require/support an a separate config file
*/
public static final String STORAGE_CONF_FILE_KEY = "conffile";
/**
* Define the storage backed to use for persistence
*/
public static final String STORAGE_BACKEND_KEY = "backend";
public static final String STORAGE_BACKEND_DEFAULT = "local";
/**
* Specifies whether write operations are supported
*/
public static final String STORAGE_READONLY_KEY = "read-only";
public static final boolean STORAGE_READONLY_DEFAULT = false;
/**
* Enables batch loading which improves write performance but assumes that only one thread is interacting with
* the graph
*/
public static final String STORAGE_BATCH_KEY = "batch-loading";
public static final boolean STORAGE_BATCH_DEFAULT = false;
/**
* Enables transactions on storage backends that support them
*/
public static final String STORAGE_TRANSACTIONAL_KEY = "transactions";
public static final boolean STORAGE_TRANSACTIONAL_DEFAULT = true;
/**
* Buffers graph mutations locally up to the specified number before persisting them against the storage backend.
* Set to 0 to disable buffering. Buffering is disabled automatically if the storage backend does not support buffered mutations.
*/
public static final String BUFFER_SIZE_KEY = "buffer-size";
public static final int BUFFER_SIZE_DEFAULT = 1024;
/**
* Number of times the database attempts to persist the transactional state to the storage layer.
* Persisting the state of a committed transaction might fail for various reasons, some of which are
* temporary such as network failures. For temporary failures, Titan will re-attempt to persist the
* state up to the number of times specified.
*/
public static final String WRITE_ATTEMPTS_KEY = "write-attempts";
public static final int WRITE_ATTEMPTS_DEFAULT = 5;
/**
* Number of times the database attempts to execute a read operation against the storage layer in the current transaction.
* A read operation might fail for various reasons, some of which are
* temporary such as network failures. For temporary failures, Titan will re-attempt to read the
* state up to the number of times specified before failing the transaction
*/
public static final String READ_ATTEMPTS_KEY = "read-attempts";
public static final int READ_ATTEMPTS_DEFAULT = 3;
/**
* Time in milliseconds that Titan waits after an unsuccessful storage attempt before retrying.
*/
public static final String STORAGE_ATTEMPT_WAITTIME_KEY = "attempt-wait";
public static final int STORAGE_ATTEMPT_WAITTIME_DEFAULT = 250;
/**
* If enabled, Titan attempts to parallelize storage operations against the storage backend using a fixed thread pool shared
* across the entire Titan graph database instance. Parallelization is only applicable to certain storage operations and
* can be beneficial when the operation is I/O bound.
*/
public static final String PARALLEL_BACKEND_OPS_KEY = "parallel-backend-ops";
public static final boolean PARALLEL_BACKEND_OPS_DEFAULT = true;
/**
* A unique identifier for the machine running the @TitanGraph@ instance.
* It must be ensured that no other machine accessing the storage backend can have the same identifier.
*/
public static final String INSTANCE_RID_RAW_KEY = "machine-id";
/**
* A locally unique identifier for a particular @TitanGraph@ instance. This only needs to be configured
* when multiple @TitanGraph@ instances are running on the same machine. A unique machine specific appendix
* guarantees a globally unique identifier.
*/
public static final String INSTANCE_RID_SHORT_KEY = "machine-id-appendix";
/**
* Number of times the system attempts to acquire a lock before giving up and throwing an exception.
*/
public static final String LOCK_RETRY_COUNT = "lock-retries";
public static final int LOCK_RETRY_COUNT_DEFAULT = 3;
/**
* The number of milliseconds the system waits for a lock application to be acknowledged by the storage backend.
* Also, the time waited at the end of all lock applications before verifying that the applications were successful.
* This value should be a small multiple of the average consistent write time.
*/
public static final String LOCK_WAIT_MS = "lock-wait-time";
public static final long LOCK_WAIT_MS_DEFAULT = 100;
/**
* Number of milliseconds after which a lock is considered to have expired. Lock applications that were not released
* are considered expired after this time and released.
* This value should be larger than the maximum time a transaction can take in order to guarantee that no correctly
* held applications are expired pre-maturely and as small as possible to avoid dead lock.
*/
public static final String LOCK_EXPIRE_MS = "lock-expiry-time";
public static final long LOCK_EXPIRE_MS_DEFAULT = 300 * 1000;
/**
* Locker type to use. The supported types are in {@link com.thinkaurelius.titan.diskstorage.Backend}.
*/
public static final String LOCK_BACKEND = "lock-backend";
public static final String LOCK_BACKEND_DEFAULT = "consistentkey";
/**
* The number of milliseconds the system waits for an id block application to be acknowledged by the storage backend.
* Also, the time waited after the application before verifying that the application was successful.
*/
public static final String IDAUTHORITY_WAIT_MS_KEY = "idauthority-wait-time";
public static final long IDAUTHORITY_WAIT_MS_DEFAULT = 300;
/**
* Number of times the system attempts to acquire a unique id block before giving up and throwing an exception.
*/
public static final String IDAUTHORITY_RETRY_COUNT_KEY = "idauthority-retries";
public static final int IDAUTHORITY_RETRY_COUNT_DEFAULT = 20;
/**
* Configures the number of bits of Titan assigned ids that are reserved for a unique id marker that
* allows the id allocation to be scaled over multiple sub-clusters and to reduce race-conditions
* when a lot of Titan instances attempt to allocate ids at the same time (e.g. during parallel bulk loading)
*
* IMPORTANT: This should never ever, ever be modified from its initial value and ALL Titan instances must use the
* same value. Otherwise, data corruption will occur.
*/
public static final String IDAUTHORITY_UNIQUE_ID_BITS_KEY = "idauthority-uniqueid-bits";
public static final int IDAUTHORITY_UNIQUE_ID_BITS_DEFAULT = 0;
/**
* Unique id marker to be used by this Titan instance when allocating ids. The unique id marker
* must be non-negative and fit within the number of unique id bits configured.
* By assigning different unique id markers to individual Titan instances it can be assured
* that those instances don't conflict with one another when attempting to allocate new id blocks.
*
* IMPORTANT: The configured unique id marker must fit within the configured unique id bit width.
*/
public static final String IDAUTHORITY_UNIQUE_ID_KEY = "idauthority-uniqueid";
public static final int IDAUTHORITY_UNIQUE_ID_DEFAULT = 0;
/**
* Configures this Titan instance to use a random unique id marker each time it attempts to allocate
* a new id block. This is an alternative to configuring {@link #IDAUTHORITY_UNIQUE_ID_KEY} where the
* actual value does not matter since one just wants to avoid id allocation conflicts among many Titan
* instances.
*
* IMPORTANT: The random unique id will be randomly generated to fit within the unique id bit width. Hence
* this option must be configured accordingly.
*/
public static final String IDAUTHORITY_RANDOMIZE_UNIQUE_ID_KEY = "idauthority-uniqueid-random";
public static final boolean IDAUTHORITY_RANDOMIZE_UNIQUE_ID_DEFAULT = false;
/**
* Configures this Titan instance to use local consistency guarantees when allocating ids. This is useful
* when Titan runs on a very large cluster of machines that is broken up into multiple local sub-clusters.
* In this case, the consistency is only ensured within the local sub-clusters which does not require
* acquiring global locks that can be too expensive to acquire.
* Using local consistency requires that a unique id marker {@link #IDAUTHORITY_UNIQUE_ID_KEY} is configured
* that fits within the bit width {@link #IDAUTHORITY_UNIQUE_ID_BITS_KEY} and that each local cluster of Titan
* instances have a unique id. In other words, no two Titan sub-cluster should have the same unique id marker.
*
* THIS IS VERY IMPORTANT. Since only local consistency is used, identical unique id marker would result in
* data corruption.
*
*/
public static final String IDAUTHORITY_USE_LOCAL_CONSISTENCY_KEY = "idauthority-local-consistency";
public static final boolean IDAUTHORITY_USE_LOCAL_CONSISTENCY_DEFAULT = false;
/**
* Configuration key for the hostname or list of hostname of remote storage backend servers to connect to.
* <p/>
* Value = {@value}
*/
public static final String HOSTNAME_KEY = "hostname";
/**
* Default hostname at which to attempt connecting to remote storage backend
* <p/>
* Value = {@value}
*/
public static final String HOSTNAME_DEFAULT = "127.0.0.1";
/**
* Configuration key for the port on which to connect to remote storage backend servers.
* <p/>
* Value = {@value}
*/
public static final String PORT_KEY = "port";
/**
* Username and password keys to be used to specify an access credential that may be needed to connect
* with a secured storage backend.
*/
public static final String AUTH_USERNAME_KEY = "username";
public static final String AUTH_PASSWORD_KEY = "password";
/**
* Default timeout when connecting to a remote database instance
* <p/>
* Value = {@value}
*/
public static final int CONNECTION_TIMEOUT_DEFAULT = 10000;
public static final String CONNECTION_TIMEOUT_KEY = "connection-timeout";
/**
* Time in milliseconds for backend manager to wait for the storage backends to
* become available when Titan is run in server mode. Should the backend manager
* experience exceptions when attempting to access the storage backend it will retry
* until this timeout is exceeded.
* <p/>
* A wait time of 0 disables waiting.
* <p/>
* Value = {@value}
*/
public static final int SETUP_WAITTIME_DEFAULT = 60000;
public static final String SETUP_WAITTIME_KEY = "setup-wait";
/**
* Default number of connections to pool when connecting to a remote database.
* <p/>
* Value = {@value}
*/
public static final int CONNECTION_POOL_SIZE_DEFAULT = 32;
public static final String CONNECTION_POOL_SIZE_KEY = "connection-pool-size";
/**
* Default number of results to pull over the wire when iterating over a distributed
* storage backend.
* This is batch size of results to pull when iterating a result set.
*/
public static final int PAGE_SIZE_DEFAULT = 100;
public static final String PAGE_SIZE_KEY = "page-size";
// ################ IDS ###########################
// ################################################
public static final String IDS_NAMESPACE = "ids";
/**
* Size of the block to be acquired. Larger block sizes require fewer block applications but also leave a larger
* fraction of the id pool occupied and potentially lost. For write heavy applications, larger block sizes should
* be chosen.
*/
public static final String IDS_BLOCK_SIZE_KEY = "block-size";
public static final int IDS_BLOCK_SIZE_DEFAULT = 10000;
/**
* Whether the id space should be partitioned for equal distribution of keys. If the keyspace is ordered, this needs to be
* enabled to ensure an even distribution of data. If the keyspace is random/hashed, then enabling this only has the benefit
* of de-congesting a single id pool in the database.
*/
public static final String IDS_PARTITION_KEY = "partition";
public static final boolean IDS_PARTITION_DEFAULT = false;
/**
* If flush ids is enabled, vertices and edges are assigned ids immediately upon creation. If not, then ids are only
* assigned when the transaction is committed.
*/
public static final String IDS_FLUSH_KEY = "flush";
public static final boolean IDS_FLUSH_DEFAULT = true;
/**
* The number of milliseconds that the Titan id pool manager will wait before giving up on allocating a new block
* of ids. Note, that failure to allocate a new id block will cause the entire database to fail, hence this value
* should be set conservatively. Choose a high value if there is a lot of contention around id allocation.
*/
public static final String IDS_RENEW_TIMEOUT_KEY = "renew-timeout";
public static final long IDS_RENEW_TIMEOUT_DEFAULT = 60 * 1000; // 1 minute
/**
* Configures when the id pool manager will attempt to allocate a new id block. When all but the configured percentage
* of the current block is consumed, a new block will be allocated. Larger values should be used if a lot of ids
* are allocated in a short amount of time. Value must be in (0,1].
*/
public static final String IDS_RENEW_BUFFER_PERCENTAGE_KEY = "renew-percentage";
public static final double IDS_RENEW_BUFFER_PERCENTAGE_DEFAULT = 0.3; // 30 %
// ############## External Index ######################
// ################################################
public static final String INDEX_NAMESPACE = "index";
/**
* Define the storage backed to use for persistence
*/
public static final String INDEX_BACKEND_KEY = "backend";
public static final String INDEX_BACKEND_DEFAULT = "lucene";
// ############## Attributes ######################
// ################################################
public static final String ATTRIBUTE_NAMESPACE = "attributes";
public static final String ATTRIBUTE_ALLOW_ALL_SERIALIZABLE_KEY = "allow-all";
public static final boolean ATTRIBUTE_ALLOW_ALL_SERIALIZABLE_DEFAULT = true;
/**
* If enabled, uses the default Kryo string serialization which is more compact
* than the binary preserving one provided by Titan ({@link com.thinkaurelius.titan.graphdb.database.serialize.attribute.StringSerializer})
*/
public static final String STRING_COMPACT_SERIALIZE = "compact-string";
public static final boolean STRING_COMPACT_SERIALIZE_DEFAULT = false;
private static final String ATTRIBUTE_PREFIX = "attribute";
private static final String SERIALIZER_PREFIX = "serializer";
// ################ Metrics #######################
// ################################################
/**
* Configuration key prefix for Metrics.
*/
public static final String METRICS_NAMESPACE = "metrics";
/**
* Whether to enable Titan metrics.
*/
public static final String METRICS_ENABLED = "enabled";
public static final boolean METRICS_ENABLED_DEFAULT = false;
/**
* Whether to enable basic timing and operation count monitoring on backend
* methods using the {@code com.codahale.metrics} package.
*
* @deprecated use {@value #METRICS_ENABLED} instead
*/
public static final String BASIC_METRICS = "enable-basic-metrics";
public static final boolean BASIC_METRICS_DEFAULT = METRICS_ENABLED_DEFAULT;
/**
* The default name prefix for Metrics reported by Titan. All metric names
* will begin with this string and a period. This value can be overridden on
* a transaction-specific basis through
* {@link StandardTransactionBuilder#setMetricsPrefix(String)}.
* <p/>
* Default = {@literal #METRICS_PREFIX_DEFAULT}
*/
public static final String METRICS_PREFIX_KEY = "prefix";
public static final String METRICS_PREFIX_DEFAULT = "com.thinkaurelius.titan";
/**
* This is the prefix used outside of a graph database configuration, or for
* operations where a system-internal transaction is necessary as an
* implementation detail. It currently can't be modified, though there is no
* substantial technical obstacle preventing it from being configured --
* some kind of configuration object is in scope everywhere it is used, and
* it could theoretically be stored in and read from that object.
*/
public static final String METRICS_SYSTEM_PREFIX_DEFAULT = METRICS_PREFIX_DEFAULT + "." + "sys";
/**
* Whether to aggregate measurements for the edge store, vertex index, edge
* index, and ID store.
* <p/>
* If true, then metrics for each of these backends will use the same metric
* name ("stores"). All of their measurements will be combined. This setting
* measures the sum of Titan's backend activity without distinguishing
* between contributions of its various internal stores.
* <p/>
* If false, then metrics for each of these backends will use a unique
* metric name ("idStore", "edgeStore", "vertexIndex", and "edgeIndex").
* This setting exposes the activity associated with each backend component,
* but it also multiplies the number of measurements involved by four.
* <p/>
* This option has no effect when {@link #BASIC_METRICS} is false.
*/
public static final String MERGE_BASIC_METRICS_KEY = "merge-basic-metrics";
public static final boolean MERGE_BASIC_METRICS_DEFAULT = true;
/**
* Metrics console reporter interval in milliseconds. Leaving this
* configuration key absent or null disables the console reporter.
*/
public static final String METRICS_CONSOLE_INTERVAL_KEY = "console.interval";
public static final Long METRICS_CONSOLE_INTERVAL_DEFAULT = null;
/**
* Metrics CSV reporter interval in milliseconds. Leaving this configuration
* key absent or null disables the CSV reporter.
*/
public static final String METRICS_CSV_INTERVAL_KEY = "csv.interval";
public static final Long METRICS_CSV_INTERVAL_DEFAULT = null;
/**
* Metrics CSV output directory. It will be created if it doesn't already
* exist. This option must be non-null if {@link #METRICS_CSV_INTERVAL_KEY} is
* non-null. This option has no effect if {@code #METRICS_CSV_INTERVAL} is
* null.
*/
public static final String METRICS_CSV_DIR_KEY = "csv.dir";
public static final String METRICS_CSV_DIR_DEFAULT = null;
/**
* Whether to report Metrics through a JMX MBean.
*/
public static final String METRICS_JMX_ENABLED_KEY = "jmx.enabled";
public static final boolean METRICS_JMX_ENABLED_DEFAULT = false;
/**
* The JMX domain in which to report Metrics. If null, then Metrics applies
* its default value.
*/
public static final String METRICS_JMX_DOMAIN_KEY = "jmx.domain";
public static final String METRICS_JMX_DOMAIN_DEFAULT = null;
/**
* The JMX agentId through which to report Metrics. Calling
* {@link MBeanServerFactory#findMBeanServer(String)} on this value must
* return exactly one {@code MBeanServer} at runtime. If null, then Metrics
* applies its default value.
*/
public static final String METRICS_JMX_AGENTID_KEY = "jmx.agentid";
public static final String METRICS_JMX_AGENTID_DEFAULT = null;
/**
* Metrics Slf4j reporter interval in milliseconds. Leaving this
* configuration key absent or null disables the Slf4j reporter.
*/
public static final String METRICS_SLF4J_INTERVAL_KEY = "slf4j.interval";
public static final Long METRICS_SLF4J_INTERVAL_DEFAULT = null;
/**
* The complete name of the Logger through which Metrics will report via
* Slf4j. If non-null, then Metrics will be dumped on
* {@link LoggerFactory#getLogger(String)} with the configured value as the
* argument. If null, then Metrics will use its default Slf4j logger.
*/
public static final String METRICS_SLF4J_LOGGER_KEY = "slf4j.logger";
public static final String METRICS_SLF4J_LOGGER_DEFAULT = null;
/**
* The configuration namespace within {@link #METRICS_NAMESPACE} for
* Ganglia.
*/
public static final String GANGLIA_NAMESPACE = "ganglia";
/**
* The unicast host or multicast group name to which Metrics will send
* Ganglia data. Setting this config key has no effect unless
* {@link #GANGLIA_INTERVAL_KEY} is also set.
*/
public static final String GANGLIA_HOST_OR_GROUP_KEY = "hostname";
/**
* The number of milliseconds to wait between sending Metrics data to the
* host or group specified by {@link #GANGLIA_HOST_OR_GROUP_KEY}. This has no
* effect unless {@link #GANGLIA_HOST_OR_GROUP_KEY} is also set.
*/
public static final String GANGLIA_INTERVAL_KEY = "interval";
/**
* The port to which Ganglia data are sent.
* <p/>
* Default = {@value #GANGLIA_PORT_DEFAULT}
*/
public static final String GANGLIA_PORT = "port";
public static final int GANGLIA_PORT_DEFAULT = 8649;
/**
* Whether to interpret {@link #GANGLIA_HOST_OR_GROUP_KEY} as a unicast or
* multicast address. If present, it must be either the string "multicast"
* or the string "unicast".
* <p/>
* Default = {@value #GANGLIA_ADDRESSING_MODE_DEFAULT}
*/
public static final String GANGLIA_ADDRESSING_MODE_KEY = "addressing-mode";
public static final String GANGLIA_ADDRESSING_MODE_DEFAULT = "unicast";
/**
* The multicast TTL to set on outgoing Ganglia datagrams. This has no
* effect when {@link #GANGLIA_ADDRESSING_MODE_KEY} is set to "multicast".
* <p/>
* Default = {@value #GANGLIA_TTL_DEFAULT}
*/
public static final String GANGLIA_TTL_KEY = "ttl";
public static final int GANGLIA_TTL_DEFAULT = 1;
/**
* Whether to send data to Ganglia in the 3.1 protocol format (true) or the
* 3.0 protocol format (false).
* <p/>
* Default = {@value #GANGLIA_USE_PROTOCOL_31_DEFAULT}
*/
public static final String GANGLIA_USE_PROTOCOL_31_KEY = "protocol-31";
public static final boolean GANGLIA_USE_PROTOCOL_31_DEFAULT = true;
/**
* The host UUID to set on outgoing Ganglia datagrams. If null, no UUID is
* set on outgoing data.
* <p/>
* See https://github.com/ganglia/monitor-core/wiki/UUIDSources
* <p/>
* Default = {@value #GANGLIA_UUID_DEFAULT}
*/
public static final String GANGLIA_UUID_KEY = "uuid";
public static final UUID GANGLIA_UUID_DEFAULT = null;
/**
* If non-null, it must be a valid Gmetric spoof string formatted as an
* IP:hostname pair. If null, Ganglia will automatically determine the IP
* and hostname to set on outgoing datagrams.
* <p/>
* See http://sourceforge.net/apps/trac/ganglia/wiki/gmetric_spoofing
* <p/>
* Default = {@value #GANGLIA_SPOOF_DEFAULT}
*/
public static final String GANGLIA_SPOOF_KEY = "spoof";
public static final String GANGLIA_SPOOF_DEFAULT = null;
/**
* The configuration namespace within {@link #METRICS_NAMESPACE} for
* Graphite.
*/
public static final String GRAPHITE_NAMESPACE = "graphite";
/**
* The hostname to receive Graphite plaintext protocol metric data. Setting
* this config key has no effect unless {@link #GRAPHITE_INTERVAL_KEY} is also
* set.
*/
public static final String GRAPHITE_HOST_KEY = "hostname";
/**
* The number of milliseconds to wait between sending Metrics data to the
* host specified {@link #GRAPHITE_HOST_KEY}. This has no effect unless
* {@link #GRAPHITE_HOST_KEY} is also set.
*/
public static final String GRAPHITE_INTERVAL_KEY = "interval";
/**
* The port to which Graphite data are sent.
* <p/>
* Default = {@value #GRAPHITE_PORT_DEFAULT}
*/
public static final String GRAPHITE_PORT_KEY = "port";
public static final int GRAPHITE_PORT_DEFAULT = 2003;
/**
* A Graphite-specific prefix for reported metrics. If non-null, Metrics
* prepends this and a "." to all metric names before reporting them to
* Graphite.
* <p/>
* Default = {@value #GRAPHITE_PREFIX_DEFAULT}
*/
public static final String GRAPHITE_PREFIX_KEY = "prefix";
public static final String GRAPHITE_PREFIX_DEFAULT = null;
private final Configuration configuration;
private boolean readOnly;
private boolean flushIDs;
private boolean batchLoading;
private int txCacheSize;
private DefaultTypeMaker defaultTypeMaker;
private Boolean propertyPrefetching;
private boolean allowVertexIdSetting;
private String metricsPrefix;
private String unknownIndexKeydName;
private StoreFeatures storeFeatures = null;
public GraphDatabaseConfiguration(String dirOrFile) {
this(new File(dirOrFile));
}
public GraphDatabaseConfiguration(File dirOrFile) {
this(getConfiguration(dirOrFile));
}
public GraphDatabaseConfiguration(Configuration config) {
Preconditions.checkNotNull(config);
this.configuration = config;
preLoadConfiguration();
}
/**
* Load a properties file containing a Titan graph configuration or create a
* stub configuration for a directory.
* <p/>
* If the argument is a file:
* <p/>
* <ol>
* <li>Load its contents into a {@link PropertiesConfiguration}</li>
* <li>For each key starting with {@link #STORAGE_NAMESPACE} and ending in
* {@link #STORAGE_DIRECTORY_KEY} or {@link #STORAGE_CONF_FILE_KEY}, check
* whether the associated value is a non-null, non-absolute path. If so,
* then prepend the absolute path of the parent directory of
* {@code dirorFile}. This has the effect of making non-absolute backend
* paths relative to the config file's directory rather than the JVM's
* working directory.
* <li>Return the {@code PropertiesConfiguration}</li>
* </ol>
* <p/>
* <p/>
* Otherwise (if the argument is not a file):
* <ol>
* <li>Create a new {@link BaseConfiguration}</li>
* <li>Set the key STORAGE_DIRECTORY_KEY in namespace STORAGE_NAMESPACE to
* the absolute path of the argument</li>
* <li>Return the {@code BaseConfiguration}</li>
*
* @param dirOrFile
* A properties file to load or directory in which to read and
* write data
* @return A configuration derived from {@code dirOrFile}
*/
@SuppressWarnings("unchecked")
public static final Configuration getConfiguration(File dirOrFile) {
Preconditions.checkNotNull(dirOrFile, "Need to specify a configuration file or storage directory");
Configuration configuration;
try {
if (dirOrFile.isFile()) {
configuration = new PropertiesConfiguration(dirOrFile);
final File tmpParent = dirOrFile.getParentFile();
final File configParent;
if (null == tmpParent) {
/*
* null usually means we were given a Titan config file path
* string like "foo.properties" that refers to the current
* working directory of the process.
*/
configParent = new File(System.getProperty("user.dir"));
} else {
configParent = tmpParent;
}
Preconditions.checkNotNull(configParent);
Preconditions.checkArgument(configParent.isDirectory());
final Pattern p = Pattern.compile(
Pattern.quote(STORAGE_NAMESPACE) + "\\..*" +
"(" +
Pattern.quote(STORAGE_DIRECTORY_KEY) + "|" +
Pattern.quote(STORAGE_CONF_FILE_KEY) +
")");
final Iterator<String> keysToMangle = Iterators.filter(configuration.getKeys(), new Predicate<String>() {
@Override
public boolean apply(String key) {
if (null == key)
return false;
return p.matcher(key).matches();
}
});
while (keysToMangle.hasNext()) {
String k = keysToMangle.next();
Preconditions.checkNotNull(k);
String s = configuration.getString(k);
if (null == s) {
log.warn("Configuration key {} has null value", k);
continue;
}
File storedir = new File(s);
if (!storedir.isAbsolute()) {
configuration.setProperty(k, configParent.getAbsolutePath() + File.separator + s);
log.debug("Overwrote relative path for key {}: was {}, now {}", k, s, configuration.getProperty(k));
} else {
log.debug("Loaded absolute path for key {}: {}", k, s);
}
}
} else {
configuration = new BaseConfiguration();
configuration.setProperty(keyInNamespace(STORAGE_NAMESPACE, STORAGE_DIRECTORY_KEY), dirOrFile.getAbsolutePath());
}
} catch (ConfigurationException e) {
throw new IllegalArgumentException("Could not load configuration at: " + dirOrFile, e);
}
return configuration;
}
public static final String toString(Configuration config) {
StringBuilder s = new StringBuilder();
Iterator<String> keys = config.getKeys();
while (keys.hasNext()) {
String key = keys.next();
s.append(key).append(": ").append(config.getProperty(key)).append("\n");
}
return s.toString();
}
public static final String getSystemMetricsPrefix() {
return METRICS_SYSTEM_PREFIX_DEFAULT;
}
private void preLoadConfiguration() {
Configuration storageConfig = configuration.subset(STORAGE_NAMESPACE);
readOnly = storageConfig.getBoolean(STORAGE_READONLY_KEY, STORAGE_READONLY_DEFAULT);
flushIDs = configuration.subset(IDS_NAMESPACE).getBoolean(IDS_FLUSH_KEY, IDS_FLUSH_DEFAULT);
batchLoading = storageConfig.getBoolean(STORAGE_BATCH_KEY, STORAGE_BATCH_DEFAULT);
txCacheSize = configuration.getInt(TX_CACHE_SIZE_KEY, TX_CACHE_SIZE_DEFAULT);
defaultTypeMaker = preregisteredAutoType.get(configuration.getString(AUTO_TYPE_KEY, AUTO_TYPE_DEFAULT));
Preconditions.checkNotNull(defaultTypeMaker, "Invalid " + AUTO_TYPE_KEY + " option: " + configuration.getString(AUTO_TYPE_KEY, AUTO_TYPE_DEFAULT));
//Disable auto-type making when batch-loading is enabled since that may overwrite types without warning
if (batchLoading) defaultTypeMaker = DisableDefaultTypeMaker.INSTANCE;
if (configuration.containsKey(PROPERTY_PREFETCHING_KEY))
propertyPrefetching = configuration.getBoolean(PROPERTY_PREFETCHING_KEY);
else propertyPrefetching = null;
allowVertexIdSetting = configuration.getBoolean(ALLOW_SETTING_VERTEX_ID_KEY, ALLOW_SETTING_VERTEX_ID_DEFAULT);
unknownIndexKeydName = configuration.getBoolean(IGNORE_UNKNOWN_INDEX_FIELD_KEY,IGNORE_UNKNOWN_INDEX_FIELD_DEFAULT)?UKNOWN_FIELD_NAME:null;
configureMetrics();
}
private void configureMetrics() {
Preconditions.checkNotNull(configuration);
final boolean enableMetrics = isMetricsEnabledInGraphConfig(configuration);
if (enableMetrics) {
Configuration metricsConf = configuration.subset(METRICS_NAMESPACE);
metricsPrefix = metricsConf.getString(METRICS_PREFIX_KEY, METRICS_PREFIX_DEFAULT);
Preconditions.checkNotNull(metricsPrefix);
configureMetricsConsoleReporter(metricsConf);
configureMetricsCsvReporter(metricsConf);
configureMetricsJmxReporter(metricsConf);
configureMetricsSlf4jReporter(metricsConf);
configureMetricsGangliaReporter(metricsConf);
configureMetricsGraphiteReporter(metricsConf);
} else {
metricsPrefix = null;
}
}
private static boolean isMetricsEnabledInGraphConfig(Configuration graphConf) {
Preconditions.checkNotNull(graphConf);
Configuration metricsConf = graphConf.subset(METRICS_NAMESPACE);
Configuration storageConf = graphConf.subset(STORAGE_NAMESPACE);
return isMetricsEnabled(storageConf, metricsConf);
}
public static boolean isMetricsEnabled(Configuration storageConf, Configuration metricsConf) {
// Even if the subset key is not present, commons config should return an object
Preconditions.checkNotNull(metricsConf);
Preconditions.checkNotNull(storageConf);
// The config option "metrics.enabled" is preferred, but the legacy
// options "metrics.enable-basic-metrics" and
// "storage.enable-basic-metrics" are also recognized
//
// Logical disjunction is the intuitive choice because the default value
// is false
return metricsConf.getBoolean(METRICS_ENABLED, METRICS_ENABLED_DEFAULT) ||
metricsConf.getBoolean(BASIC_METRICS, BASIC_METRICS_DEFAULT) ||
storageConf.getBoolean(BASIC_METRICS, BASIC_METRICS_DEFAULT);
}
public static boolean isMetricsMergingEnabled(Configuration storageConf, Configuration metricsConf) {
// Even if the subset key is not present, commons config should return an object
Preconditions.checkNotNull(metricsConf);
Preconditions.checkNotNull(storageConf);
// Recognize either "metrics.merge-basic-metrics" or
// "storage.merge-basic-metrics".
//
// Logical conjunction is the intuitive choice because the default value
// is true
return metricsConf.getBoolean(MERGE_BASIC_METRICS_KEY, MERGE_BASIC_METRICS_DEFAULT) &&
storageConf.getBoolean(MERGE_BASIC_METRICS_KEY, MERGE_BASIC_METRICS_DEFAULT);
}
private void configureMetricsConsoleReporter(Configuration conf) {
Long ms = conf.getLong(METRICS_CONSOLE_INTERVAL_KEY, METRICS_CONSOLE_INTERVAL_DEFAULT);
if (null != ms) {
log.debug("Console metrics on");
MetricManager.INSTANCE.addConsoleReporter(ms);
} else {
log.debug("Console metrics off");
}
}
private void configureMetricsCsvReporter(Configuration conf) {
Long ms = conf.getLong(METRICS_CSV_INTERVAL_KEY, METRICS_CONSOLE_INTERVAL_DEFAULT);
String out = conf.getString(METRICS_CSV_DIR_KEY, METRICS_CSV_DIR_DEFAULT);
if (null != ms && null != out) {
MetricManager.INSTANCE.addCsvReporter(ms, out);
}
}
private void configureMetricsJmxReporter(Configuration conf) {
boolean enabled = conf.getBoolean(METRICS_JMX_ENABLED_KEY, METRICS_JMX_ENABLED_DEFAULT);
String domain = conf.getString(METRICS_JMX_DOMAIN_KEY, METRICS_JMX_DOMAIN_DEFAULT);
String agentId = conf.getString(METRICS_JMX_AGENTID_KEY, METRICS_JMX_AGENTID_DEFAULT);
if (enabled) {
MetricManager.INSTANCE.addJmxReporter(domain, agentId);
}
}
private void configureMetricsSlf4jReporter(Configuration conf) {
Long ms = conf.getLong(METRICS_SLF4J_INTERVAL_KEY, METRICS_SLF4J_INTERVAL_DEFAULT);
// null loggerName is allowed -- that means Metrics will use its internal default
String loggerName = conf.getString(METRICS_SLF4J_LOGGER_KEY, METRICS_SLF4J_LOGGER_DEFAULT);
if (null != ms) {
MetricManager.INSTANCE.addSlf4jReporter(ms, loggerName);
}
}
private void configureMetricsGangliaReporter(Configuration conf) {
Configuration ganglia = conf.subset(GANGLIA_NAMESPACE);
if (null == ganglia)
return;
final String host = ganglia.getString(GANGLIA_HOST_OR_GROUP_KEY, null);
final Long ms = ganglia.getLong(GANGLIA_INTERVAL_KEY, null);
if (null == host || null == ms) {
return;
}
final Integer port = ganglia.getInt(GANGLIA_PORT, GANGLIA_PORT_DEFAULT);
final UDPAddressingMode addrMode;
final String addrModeStr = ganglia.getString(GANGLIA_ADDRESSING_MODE_KEY, GANGLIA_ADDRESSING_MODE_DEFAULT);
if (addrModeStr.toLowerCase().equals("multicast")) {
addrMode = UDPAddressingMode.MULTICAST;
} else if (addrModeStr.toLowerCase().equals("unicast")) {
addrMode = UDPAddressingMode.UNICAST;
} else {
throw new RuntimeException("Invalid setting " + METRICS_NAMESPACE
+ "." + GANGLIA_NAMESPACE + "." + GANGLIA_ADDRESSING_MODE_KEY
+ "=\"" + addrModeStr
+ "\": must be \"unicast\" or \"multicast\"");
}
final Boolean proto31 = ganglia.getBoolean(GANGLIA_USE_PROTOCOL_31_KEY, GANGLIA_USE_PROTOCOL_31_DEFAULT);
final int ttl = ganglia.getInt(GANGLIA_TTL_KEY, GANGLIA_TTL_DEFAULT);
final UUID uuid;
final String uuidStr = ganglia.getString(GANGLIA_UUID_KEY);
if (null != uuidStr) {
uuid = UUID.fromString(uuidStr);
} else {
uuid = GANGLIA_UUID_DEFAULT;
}
String spoof = ganglia.getString(GANGLIA_SPOOF_KEY, GANGLIA_SPOOF_DEFAULT);
if (null != spoof && 0 > spoof.indexOf(':')) {
throw new RuntimeException("Invalid setting " + METRICS_NAMESPACE
+ "." + GANGLIA_NAMESPACE + "." + GANGLIA_SPOOF_KEY + "=\""
+ spoof + "\": must be formatted as \"IP:hostname\"");
}
try {
MetricManager.INSTANCE.addGangliaReporter(host, port, addrMode, ttl, proto31, uuid, spoof, ms);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void configureMetricsGraphiteReporter(Configuration conf) {
Configuration graphite = conf.subset(GRAPHITE_NAMESPACE);
if (null == graphite)
return;
final String host = graphite.getString(GRAPHITE_HOST_KEY, null);
final Long ms = graphite.getLong(GRAPHITE_INTERVAL_KEY, null);
if (null == host || null == ms) {
return;
}
final Integer port = graphite.getInt(GRAPHITE_PORT_KEY, GRAPHITE_PORT_DEFAULT);
final String prefix = graphite.getString(GRAPHITE_PREFIX_KEY, GRAPHITE_PREFIX_DEFAULT);
MetricManager.INSTANCE.addGraphiteReporter(host, port, prefix, ms);
}
public boolean isReadOnly() {
return readOnly;
}
public boolean hasFlushIDs() {
return flushIDs;
}
public int getTxCacheSize() {
return txCacheSize;
}
public boolean isBatchLoading() {
return batchLoading;
}
public String getMetricsPrefix() {
return metricsPrefix;
}
public DefaultTypeMaker getDefaultTypeMaker() {
return defaultTypeMaker;
}
public boolean allowVertexIdSetting() {
return allowVertexIdSetting;
}
public boolean hasPropertyPrefetching() {
if (propertyPrefetching == null) {
return getStoreFeatures().isDistributed();
} else {
return propertyPrefetching;
}
}
public String getUnknownIndexKeydName() {
return unknownIndexKeydName;
}
public int getWriteAttempts() {
int attempts = configuration.subset(STORAGE_NAMESPACE).getInt(WRITE_ATTEMPTS_KEY, WRITE_ATTEMPTS_DEFAULT);
Preconditions.checkArgument(attempts > 0, "Write attempts must be positive");
return attempts;
}
public int getReadAttempts() {
int attempts = configuration.subset(STORAGE_NAMESPACE).getInt(READ_ATTEMPTS_KEY, READ_ATTEMPTS_DEFAULT);
Preconditions.checkArgument(attempts > 0, "Read attempts must be positive");
return attempts;
}
public int getStorageWaittime() {
int time = configuration.subset(STORAGE_NAMESPACE).getInt(STORAGE_ATTEMPT_WAITTIME_KEY, STORAGE_ATTEMPT_WAITTIME_DEFAULT);
Preconditions.checkArgument(time > 0, "Persistence attempt retry wait time must be positive");
return time;
}
public static List<RegisteredAttributeClass<?>> getRegisteredAttributeClasses(Configuration config) {
List<RegisteredAttributeClass<?>> all = new ArrayList<RegisteredAttributeClass<?>>();
Iterator<String> iter = config.getKeys();
while (iter.hasNext()) {
String key = iter.next();
if (!key.startsWith(ATTRIBUTE_PREFIX)) continue;
try {
int position = Integer.parseInt(key.substring(ATTRIBUTE_PREFIX.length()));
Class<?> clazz = null;
AttributeHandler<?> serializer = null;
String classname = config.getString(key);
try {
clazz = Class.forName(classname);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not find attribute class" + classname, e);
}
Preconditions.checkNotNull(clazz);
if (config.containsKey(SERIALIZER_PREFIX + position)) {
String serializername = config.getString(SERIALIZER_PREFIX + position);
try {
Class sclass = Class.forName(serializername);
serializer = (AttributeHandler) sclass.newInstance();
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Could not find serializer class" + serializername);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Could not instantiate serializer class" + serializername, e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Could not instantiate serializer class" + serializername, e);
}
}
RegisteredAttributeClass reg = new RegisteredAttributeClass(clazz, serializer, position);
for (int i = 0; i < all.size(); i++) {
if (all.get(i).equals(reg)) {
throw new IllegalArgumentException("Duplicate attribute registration: " + all.get(i) + " and " + reg);
}
}
all.add(reg);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid attribute definition: " + key, e);
}
}
Collections.sort(all);
return all;
}
public VertexIDAssigner getIDAssigner(Backend backend) {
return new VertexIDAssigner(configuration.subset(IDS_NAMESPACE), backend.getIDAuthority(), backend.getStoreFeatures());
}
public String getBackendDescription() {
Configuration storageconfig = configuration.subset(STORAGE_NAMESPACE);
String clazzname = storageconfig.getString(STORAGE_BACKEND_KEY, STORAGE_BACKEND_DEFAULT);
if (storageconfig.containsKey(HOSTNAME_KEY)) {
return clazzname + ":" + storageconfig.getString(HOSTNAME_KEY);
} else {
return clazzname + ":" + storageconfig.getString(STORAGE_DIRECTORY_KEY);
}
}
public Backend getBackend() {
Configuration storageconfig = configuration.subset(STORAGE_NAMESPACE);
Configuration metricsconfig = configuration.subset(METRICS_NAMESPACE);
Backend backend = new Backend(storageconfig, metricsconfig);
backend.initialize(storageconfig);
storeFeatures = backend.getStoreFeatures();
return backend;
}
public StoreFeatures getStoreFeatures() {
Preconditions.checkArgument(storeFeatures != null, "Cannot retrieve store features before the storage backend has been initialized");
return storeFeatures;
}
public StoreCache getEdgeStoreCache() {
Configuration cacheconf = configuration.subset(CACHE_NAMESPACE);
if (this.batchLoading || !cacheconf.getBoolean(DB_CACHE_KEY,DB_CACHE_DEFAULT))
return new PassThroughStoreCache();
long expirationTime = cacheconf.getLong(DB_CACHE_TIME_KEY,DB_CACHE_TIME_DEFAULT);
Preconditions.checkArgument(expirationTime>=0,"Invalid cache expiration time: %s",expirationTime);
if (expirationTime==0) expirationTime=ETERNAL_CACHE_EXPIRATION;
long cacheSizeBytes;
double cachesize = cacheconf.getDouble(DB_CACHE_SIZE_KEY,DB_CACHE_SIZE_DEFAULT);
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 edge store cache size: {}",cacheSizeBytes);
return new ExpirationStoreCache(expirationTime,
cacheconf.getLong(DB_CACHE_CLEAN_WAIT_KEY,DB_CACHE_CLEAN_WAIT_DEFAULT),
cacheSizeBytes);
}
public Serializer getSerializer() {
Configuration config = configuration.subset(ATTRIBUTE_NAMESPACE);
Serializer serializer = new KryoSerializer(config);
for (RegisteredAttributeClass<?> clazz : getRegisteredAttributeClasses(config)) {
clazz.registerWith(serializer);
}
return serializer;
}
public boolean hasSerializeAll() {
return configuration.subset(ATTRIBUTE_NAMESPACE).getBoolean(ATTRIBUTE_ALLOW_ALL_SERIALIZABLE_KEY, ATTRIBUTE_ALLOW_ALL_SERIALIZABLE_DEFAULT);
}
public static final String keyInNamespace(String namespace, String key) {
return namespace + "." + key;
}
/* ----------------------------------------
Methods for writing/reading config files
-------------------------------------------*/
/**
* Returns the home directory for the graph database initialized in this configuration
*
* @return Home directory for this graph database configuration
*/
public File getHomeDirectory() {
if (!configuration.containsKey(keyInNamespace(STORAGE_NAMESPACE, STORAGE_DIRECTORY_KEY)))
throw new UnsupportedOperationException("No home directory specified");
File dir = new File(configuration.getString(keyInNamespace(STORAGE_NAMESPACE, STORAGE_DIRECTORY_KEY)));
Preconditions.checkArgument(dir.isDirectory(), "Not a directory");
return dir;
}
//TODO: which of the following methods are really needed
/**
* Returns the home directory path for the graph database initialized in this configuration
*
* @return Home directory path for this graph database configuration
*/
public String getHomePath() {
return getPath(getHomeDirectory());
}
private static File getSubDirectory(String base, String sub) {
File subdir = new File(base, sub);
if (!subdir.exists()) {
if (!subdir.mkdir()) {
throw new IllegalArgumentException("Cannot create subdirectory: " + sub);
}
}
assert subdir.exists() && subdir.isDirectory();
return subdir;
}
private static String getFileName(String dir, String file) {
if (!dir.endsWith(File.separator)) dir = dir + File.separator;
return dir + file;
}
public static String getPath(File dir) {
return dir.getAbsolutePath() + File.separator;
}
static boolean existsFile(String file) {
return (new File(file)).isFile();
}
static PropertiesConfiguration getPropertiesConfig(String file) {
PropertiesConfiguration config = new PropertiesConfiguration();
if (existsFile(file)) {
try {
config.load(file);
} catch (ConfigurationException e) {
throw new IllegalArgumentException("Cannot load existing configuration file", e);
}
}
config.setFileName(file);
config.setAutoSave(true);
return config;
}
private static final char CONFIGURATION_SEPARATOR = '.';
public static Set<String> getUnqiuePrefixes(Configuration config) {
Set<String> names = new HashSet<String>();
Iterator<String> keyiter = config.getKeys();
while (keyiter.hasNext()) {
String key = keyiter.next();
int pos = key.indexOf(CONFIGURATION_SEPARATOR);
if (pos > 0) names.add(key.substring(0, pos));
}
return names;
}
}