package er.neo4jadaptor.database.pool;
import java.util.HashMap;
import java.util.Map;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import er.extensions.foundation.ERXProperties;
/**
* Provides database instances with all its settings, except for the filesystem location, being read from
* system properties. Additionally it can perform database warm up, which can be useful especially in
* cases where the entire database is cached in memory.
* <p>
* Configuration system properties:
* <ul>
* <li><code>{@value #DBTYPE_PROPERTY_KEY}</code> - database type label. Possible values are described in {@link DatabaseFactoryTypeLabels}.</li>
* <li><code>{@value #CONFIG_PROPERTY_KEY}</code> - Neo4J database configuration dictionary</li>
* <li><code>{@value #WARMUP_PROPERTY_KEY}</code> - boolean flag denoting whether to perform initial database warm up (iterates over all properties for
* all nodes), defaults to {@value #WARMUP_DEFAULT_VALUE}</li>
* </ul>
*
*
* @author Jedrzej Sobanski
*/
public class DatabasePool {
private static final Logger log = LoggerFactory.getLogger(DatabasePool.class);
/**
* Singleton instance.
*/
public static final DatabasePool instance = new DatabasePool();
/**
* System property name for database {@link DatabaseFactoryType} label.
*
* @see DatabaseFactoryTypeLabels
*/
public static final String DBTYPE_PROPERTY_KEY = "neo4j.pool.database.type";
/**
* System property name for Neo4J configuration dictionary.
*/
public static final String CONFIG_PROPERTY_KEY = "neo4j.pool.database.config";
/**
* System property name for db warmup.
*/
public static final String WARMUP_PROPERTY_KEY = "neo4j.pool.doWarmUp";
/**
* Default value for db warmup setting.
*/
public static final boolean WARMUP_DEFAULT_VALUE = false;
private final Map<String, GraphDatabaseService> map = new HashMap<>();
private final boolean WARMUP_DATABASE = ERXProperties.booleanForKeyWithDefault(WARMUP_PROPERTY_KEY, WARMUP_DEFAULT_VALUE);
private final String databaseType = ERXProperties.stringForKey(DBTYPE_PROPERTY_KEY);
private DatabasePool() {
}
/**
* Gets database instance configured as in system properties operating at the given URL.
*
* @param url database url
* @return database
*/
public GraphDatabaseService get(String url) {
if (! url.startsWith("file://")) {
throw new IllegalArgumentException("Only URLs for file protocol (starting with 'file://') are supported");
}
String path = url.substring("file://".length());
GraphDatabaseService db = map.get(url);
if (db == null) {
Map<String, String> config = getConfig();
DatabaseFactoryType factoryType = DatabaseFactoryType.getForLabel(databaseType);
try {
db = factoryType.getFactory().get(path, config);
} catch (RuntimeException e) {
log.error("Failed to create database", e);
throw e;
}
if (WARMUP_DATABASE) {
warmUp(db);
}
registerShutdownHook(db);
map.put(url, db);
}
return db;
}
@SuppressWarnings("deprecation")
private void warmUp(GraphDatabaseService db) {
for (Node n : db.getAllNodes()) {
for (String s : n.getPropertyKeys()) {
n.getProperty(s);
}
for (Relationship r : n.getRelationships()) {
for (String s : r.getPropertyKeys()) {
r.getProperty(s);
}
}
}
}
@SuppressWarnings("unchecked")
private Map<String, String> getConfig() {
return ERXProperties.dictionaryForKey(CONFIG_PROPERTY_KEY);
}
private void registerShutdownHook(final GraphDatabaseService graphDb) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
graphDb.shutdown();
}
});
}
}