diff --git a/.travis.yml b/.travis.yml
index fbb01bb43..797c7036b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,7 @@
sudo: false
language: java
-jdk: openjdk8
+jdk: openjdk11
cache:
directories:
diff --git a/Dockerfile b/Dockerfile
index c34587763..f21178be0 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# Docker file for EHRI backend web service
-FROM neo4j:3.5.28
+FROM neo4j:4.2.9
# Set git commit build revision as a label which
# we can inspect to figure out the image version.
diff --git a/ehri-cli/src/main/java/eu/ehri/project/commands/GenSchema.java b/ehri-cli/src/main/java/eu/ehri/project/commands/GenSchema.java
index fc443da55..53d3a81cb 100644
--- a/ehri-cli/src/main/java/eu/ehri/project/commands/GenSchema.java
+++ b/ehri-cli/src/main/java/eu/ehri/project/commands/GenSchema.java
@@ -24,6 +24,8 @@
import eu.ehri.project.core.impl.Neo4jGraphManager;
import eu.ehri.project.core.impl.neo4j.Neo4j2Graph;
import org.apache.commons.cli.CommandLine;
+import org.neo4j.graphdb.GraphDatabaseService;
+import org.neo4j.graphdb.Transaction;
/**
* Command for generating the (Neo4j) graph schema.
@@ -47,8 +49,11 @@ public String getHelp() {
public int execWithOptions(FramedGraph> graph, CommandLine cmdLine) throws Exception {
Graph baseGraph = graph.getBaseGraph();
if (baseGraph instanceof Neo4j2Graph) {
- Neo4jGraphManager.createIndicesAndConstraints(
- ((Neo4j2Graph) baseGraph).getRawGraph());
+ GraphDatabaseService service = ((Neo4j2Graph) baseGraph).getRawGraph();
+ try (Transaction tx = service.beginTx()) {
+ Neo4jGraphManager.dropIndicesAndConstraints(tx);
+ Neo4jGraphManager.createIndicesAndConstraints(tx);
+ }
} else {
logger.warn("Cannot generate schema on a non-Neo4j2 graph");
}
diff --git a/ehri-cli/src/test/java/eu/ehri/project/commands/GraphSONTest.java b/ehri-cli/src/test/java/eu/ehri/project/commands/GraphSONTest.java
index e8f1c4870..7b86d27f4 100644
--- a/ehri-cli/src/test/java/eu/ehri/project/commands/GraphSONTest.java
+++ b/ehri-cli/src/test/java/eu/ehri/project/commands/GraphSONTest.java
@@ -66,6 +66,7 @@ public void testSaveDumpAndRead() throws Exception {
assertEquals(0, graphSON.execWithOptions(graph1, outCmdLine));
graph1.shutdown();
+ resetGraph();
assertTrue(temp.exists());
assertTrue(temp.length() > 0L);
diff --git a/ehri-core/pom.xml b/ehri-core/pom.xml
index 83749ebbf..0055f81f7 100644
--- a/ehri-core/pom.xml
+++ b/ehri-core/pom.xml
@@ -132,6 +132,18 @@
test-jar
test
+
+ org.neo4j.test
+ neo4j-harness
+ ${neo4j.version}
+ test
+
+
+ org.slf4j
+ slf4j-nop
+
+
+
org.neo4j.community
it-test-support
diff --git a/ehri-core/src/main/java/eu/ehri/project/core/impl/Neo4jGraphManager.java b/ehri-core/src/main/java/eu/ehri/project/core/impl/Neo4jGraphManager.java
index 7df2aecd2..7b55c27dd 100644
--- a/ehri-core/src/main/java/eu/ehri/project/core/impl/Neo4jGraphManager.java
+++ b/ehri-core/src/main/java/eu/ehri/project/core/impl/Neo4jGraphManager.java
@@ -31,8 +31,8 @@
import eu.ehri.project.models.EntityClass;
import eu.ehri.project.models.annotations.EntityType;
import eu.ehri.project.models.utils.ClassUtils;
-import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
+import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.graphdb.schema.Schema;
@@ -101,7 +101,6 @@ public Vertex updateVertex(String id, EntityClass type,
@Override
public void initialize() {
- createIndicesAndConstraints(graph.getBaseGraph().getRawGraph());
}
@Override
@@ -127,19 +126,27 @@ public Vertex setLabels(Vertex vertex) {
}
/**
- * Create the graph schema
+ * Drop the existing schema.
*
- * @param graph the underlying graph service
+ * @param tx the underlying graph transactions
*/
- public static void createIndicesAndConstraints(GraphDatabaseService graph) {
- Schema schema = graph.schema();
+ public static void dropIndicesAndConstraints(Transaction tx) {
+ Schema schema = tx.schema();
for (ConstraintDefinition constraintDefinition : schema.getConstraints()) {
constraintDefinition.drop();
}
for (IndexDefinition indexDefinition : schema.getIndexes()) {
indexDefinition.drop();
}
+ }
+ /**
+ * Create the graph schema
+ *
+ * @param tx the underlying graph transaction
+ */
+ public static void createIndicesAndConstraints(Transaction tx) {
+ Schema schema = tx.schema();
schema.constraintFor(Label.label(BASE_LABEL))
.assertPropertyIsUnique(EntityType.ID_KEY)
.create();
diff --git a/ehri-core/src/main/java/eu/ehri/project/core/impl/TxNeo4jGraph.java b/ehri-core/src/main/java/eu/ehri/project/core/impl/TxNeo4jGraph.java
index a58112485..6b3e9950f 100644
--- a/ehri-core/src/main/java/eu/ehri/project/core/impl/TxNeo4jGraph.java
+++ b/ehri-core/src/main/java/eu/ehri/project/core/impl/TxNeo4jGraph.java
@@ -22,6 +22,7 @@
import eu.ehri.project.core.Tx;
import eu.ehri.project.core.TxGraph;
import eu.ehri.project.core.impl.neo4j.Neo4j2Graph;
+import org.neo4j.dbms.api.DatabaseManagementService;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.slf4j.Logger;
@@ -41,20 +42,15 @@ public static class UnderlyingTxRemovedError extends RuntimeException {
public static final Logger logger = LoggerFactory.getLogger(TxNeo4jGraph.class);
- public TxNeo4jGraph(String directory) {
- super(directory);
+ public TxNeo4jGraph(DatabaseManagementService service, GraphDatabaseService graph) {
+ super(service, graph);
}
- public TxNeo4jGraph(GraphDatabaseService rawGraph) {
- super(rawGraph);
+ public TxNeo4jGraph(String directory) {
+ super(directory);
}
- private ThreadLocal etx = new ThreadLocal() {
- @Override
- public Neo4jTx initialValue() {
- return null;
- }
- };
+ private final ThreadLocal etx = ThreadLocal.withInitial(() -> null);
public Tx beginTx() {
logger.trace("Begin tx: {}", Thread.currentThread().getName());
@@ -89,7 +85,7 @@ public void commit() {
@Override
public void shutdown() {
- getRawGraph().shutdown();
+ getManagementService().shutdown();
}
/**
@@ -102,18 +98,6 @@ public void autoStartTransaction(boolean forWrite) {
// Not allowing auto-start TX
}
- /**
- * Since we override autoStartTx to do nothing, we must also
- * override this function (which loads key indices and sneakily
- * commits them) when the graph is constructed. We do not use
- * key indexable functionality so nothing is lost (unless the
- * base class is changed, but, can't do much about that.)
- */
- @Override
- public void init() {
- // Don't load key indices
- }
-
/**
* Checks if the graph is currently in a transaction.
*
@@ -130,7 +114,7 @@ public class Neo4jTx implements Tx {
*
* @return a Neo4j transaction
*/
- Transaction underlying() {
+ public Transaction underlying() {
return tx.get();
}
@@ -140,7 +124,7 @@ public void success() {
if (transaction == null) {
throw new UnderlyingTxRemovedError("Underlying transaction removed!");
}
- transaction.success();
+ transaction.commit();
}
public void close() {
@@ -160,7 +144,7 @@ public void failure() {
if (transaction == null) {
throw new UnderlyingTxRemovedError("Underlying transaction removed!");
}
- transaction.failure();
+ transaction.rollback();
}
}
-}
+}
\ No newline at end of file
diff --git a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2EdgeIterable.java b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2EdgeIterable.java
index 5ec2b33da..45bd81046 100644
--- a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2EdgeIterable.java
+++ b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2EdgeIterable.java
@@ -4,29 +4,29 @@
import com.tinkerpop.blueprints.CloseableIterable;
import com.tinkerpop.blueprints.Edge;
import org.neo4j.graphdb.Relationship;
-import org.neo4j.graphdb.index.IndexHits;
+import org.neo4j.graphdb.ResourceIterable;
import java.util.Iterator;
public class Neo4j2EdgeIterable implements CloseableIterable {
- private final Iterable relationships;
+ private final ResourceIterable relationships;
private final Neo4j2Graph graph;
@Deprecated
- public Neo4j2EdgeIterable(Iterable relationships, Neo4j2Graph graph, boolean checkTransaction) {
+ public Neo4j2EdgeIterable(ResourceIterable relationships, Neo4j2Graph graph, boolean checkTransaction) {
this(relationships, graph);
}
- public Neo4j2EdgeIterable(Iterable relationships, Neo4j2Graph graph) {
+ public Neo4j2EdgeIterable(ResourceIterable relationships, Neo4j2Graph graph) {
this.relationships = relationships;
this.graph = graph;
}
public Iterator iterator() {
graph.autoStartTransaction(true);
- return new Iterator() {
+ return new Iterator<>() {
private final Iterator itty = relationships.iterator();
public void remove() {
@@ -47,8 +47,6 @@ public boolean hasNext() {
}
public void close() {
- if (this.relationships instanceof IndexHits) {
- ((IndexHits>) this.relationships).close();
- }
+ this.relationships.iterator().close();
}
}
\ No newline at end of file
diff --git a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Element.java b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Element.java
index 65ce19f44..982077eb1 100644
--- a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Element.java
+++ b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Element.java
@@ -5,22 +5,18 @@
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.util.ElementHelper;
+import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Node;
-import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
+import java.util.*;
abstract class Neo4j2Element implements Element {
protected final Neo4j2Graph graph;
- protected PropertyContainer rawElement;
+ protected Entity rawElement;
public Neo4j2Element(Neo4j2Graph graph) {
this.graph = graph;
@@ -63,7 +59,7 @@ public int hashCode() {
return this.getId().hashCode();
}
- public PropertyContainer getRawElement() {
+ public Entity getRawElement() {
return this.rawElement;
}
diff --git a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Graph.java b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Graph.java
index 2e4329ace..96e90e7b1 100644
--- a/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Graph.java
+++ b/ehri-core/src/main/java/eu/ehri/project/core/impl/neo4j/Neo4j2Graph.java
@@ -1,57 +1,32 @@
package eu.ehri.project.core.impl.neo4j;
-import com.google.common.base.Preconditions;
-import com.tinkerpop.blueprints.CloseableIterable;
-import com.tinkerpop.blueprints.Edge;
-import com.tinkerpop.blueprints.Features;
-import com.tinkerpop.blueprints.GraphQuery;
-import com.tinkerpop.blueprints.MetaGraph;
-import com.tinkerpop.blueprints.TransactionalGraph;
-import com.tinkerpop.blueprints.Vertex;
-import com.tinkerpop.blueprints.util.DefaultGraphQuery;
-import com.tinkerpop.blueprints.util.ExceptionFactory;
-import com.tinkerpop.blueprints.util.PropertyFilteredIterable;
-import com.tinkerpop.blueprints.util.StringFactory;
-import com.tinkerpop.blueprints.util.WrappingCloseableIterable;
+import com.tinkerpop.blueprints.*;
+import com.tinkerpop.blueprints.util.*;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationConverter;
-import org.neo4j.graphdb.GraphDatabaseService;
-import org.neo4j.graphdb.Label;
-import org.neo4j.graphdb.Node;
-import org.neo4j.graphdb.NotFoundException;
-import org.neo4j.graphdb.Relationship;
-import org.neo4j.graphdb.RelationshipType;
-import org.neo4j.graphdb.ResourceIterable;
-import org.neo4j.graphdb.ResourceIterator;
-import org.neo4j.graphdb.Transaction;
-import org.neo4j.graphdb.TransactionFailureException;
-import org.neo4j.graphdb.factory.GraphDatabaseBuilder;
-import org.neo4j.graphdb.factory.GraphDatabaseFactory;
-
-import java.io.File;
+import org.neo4j.dbms.api.DatabaseManagementService;
+import org.neo4j.dbms.api.DatabaseManagementServiceBuilder;
+import org.neo4j.graphdb.*;
+
+import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Logger;
+import static org.neo4j.configuration.GraphDatabaseSettings.DEFAULT_DATABASE_NAME;
+
/**
* A Blueprints implementation of the graph database Neo4j (http://neo4j.org)
*/
public class Neo4j2Graph implements TransactionalGraph, MetaGraph {
private static final Logger logger = Logger.getLogger(Neo4j2Graph.class.getName());
+ private DatabaseManagementService managementService;
private GraphDatabaseService rawGraph;
- protected final ThreadLocal tx = new ThreadLocal() {
- protected Transaction initialValue() {
- return null;
- }
- };
+ protected final ThreadLocal tx = ThreadLocal.withInitial(() -> null);
- protected final ThreadLocal checkElementsInTransaction = new ThreadLocal() {
- protected Boolean initialValue() {
- return false;
- }
- };
+ protected final ThreadLocal checkElementsInTransaction = ThreadLocal.withInitial(() -> false);
private static final Features FEATURES = new Features();
@@ -102,30 +77,23 @@ public Neo4j2Graph(String directory) {
this(directory, null);
}
- public Neo4j2Graph(GraphDatabaseService rawGraph) {
- this.rawGraph = rawGraph;
-
- init();
- }
-
public Neo4j2Graph(String directory, Map configuration) {
try {
- GraphDatabaseBuilder builder = new GraphDatabaseFactory().newEmbeddedDatabaseBuilder(new File(directory));
- if (null != configuration)
- this.rawGraph = builder.setConfig(configuration).newGraphDatabase();
- else
- this.rawGraph = builder.newGraphDatabase();
-
- init();
-
+ DatabaseManagementServiceBuilder builder = new DatabaseManagementServiceBuilder(Paths.get(directory));
+ builder = (configuration != null) ? builder.setConfigRaw(configuration) : builder;
+ this.managementService = builder.build();
+ this.rawGraph = managementService.database( DEFAULT_DATABASE_NAME );
} catch (Exception e) {
- if (this.rawGraph != null)
- this.rawGraph.shutdown();
+ if (this.rawGraph != null) {
+ managementService.shutdown();
+ }
throw new RuntimeException(e.getMessage(), e);
}
}
- protected void init() {
+ public Neo4j2Graph(DatabaseManagementService service, GraphDatabaseService rawGraph) {
+ this.managementService = service;
+ this.rawGraph = rawGraph;
}
public Neo4j2Graph(Configuration configuration) {
@@ -136,7 +104,7 @@ public Neo4j2Graph(Configuration configuration) {
@Override
public Neo4j2Vertex addVertex(Object id) {
this.autoStartTransaction(true);
- return new Neo4j2Vertex(this.rawGraph.createNode(), this);
+ return new Neo4j2Vertex(getTransaction().createNode(), this);
}
@Override
@@ -154,7 +122,7 @@ else if (id instanceof Number)
longId = ((Number) id).longValue();
else
longId = Double.valueOf(id.toString()).longValue();
- return new Neo4j2Vertex(this.rawGraph.getNodeById(longId), this);
+ return new Neo4j2Vertex(getTransaction().getNodeById(longId), this);
} catch (NotFoundException e) {
return null;
} catch (NumberFormatException e) {
@@ -177,12 +145,20 @@ else if (id instanceof Number)
@Override
public CloseableIterable getVertices() {
this.autoStartTransaction(false);
- return new Neo4j2VertexIterable(rawGraph.getAllNodes(), this);
+ return new Neo4j2VertexIterable(getTransaction().getAllNodes(), this);
+ }
+
+ private Transaction getTransaction() {
+ Transaction tx = this.tx.get();
+ if (tx == null) {
+ throw new NotInTransactionException();
+ }
+ return tx;
}
public CloseableIterable getVerticesByLabel(final String label) {
this.autoStartTransaction(false);
- ResourceIterable wrap = () -> rawGraph.findNodes(Label.label(label));
+ ResourceIterable wrap = () -> getTransaction().findNodes(Label.label(label));
return new Neo4j2VertexIterable(wrap, this);
}
@@ -190,7 +166,7 @@ public CloseableIterable getVerticesByLabelKeyValue(
final String label, final String key, final Object value) {
ResourceIterable wrap = () -> {
autoStartTransaction(false);
- return rawGraph.findNodes(Label.label(label), key, value);
+ return getTransaction().findNodes(Label.label(label), key, value);
};
return new Neo4j2VertexIterable(wrap, this);
}
@@ -216,11 +192,11 @@ public CloseableIterable getVertices(String key, Object value) {
@Override
public CloseableIterable getEdges() {
this.autoStartTransaction(false);
- return new Neo4j2EdgeIterable(rawGraph.getAllRelationships(), this);
+ return new Neo4j2EdgeIterable(getTransaction().getAllRelationships(), this);
}
@Override
- public Iterable getEdges(String key, Object value) {
+ public CloseableIterable getEdges(String key, Object value) {
this.autoStartTransaction(false);
return new PropertyFilteredIterable<>(key, value, this.getEdges());
}
@@ -262,7 +238,7 @@ public Neo4j2Edge getEdge(Object id) {
longId = (Long) id;
else
longId = Double.valueOf(id.toString()).longValue();
- return new Neo4j2Edge(this.rawGraph.getRelationshipById(longId), this);
+ return new Neo4j2Edge(getTransaction().getRelationshipById(longId), this);
} catch (NotFoundException e) {
return null;
} catch (NumberFormatException e) {
@@ -291,7 +267,7 @@ public void commit() {
}
try {
- tx.get().success();
+ tx.get().commit();
} finally {
tx.get().close();
tx.remove();
@@ -305,7 +281,7 @@ public void rollback() {
}
try {
- tx.get().failure();
+ tx.get().rollback();
} finally {
tx.get().close();
tx.remove();
@@ -320,7 +296,7 @@ public void shutdown() {
logger.warning("Failure on shutdown " + e.getMessage());
// TODO: inspect why certain transactions fail
}
- this.rawGraph.shutdown();
+ managementService.shutdown();
}
// The forWrite flag is true when the autoStartTransaction method is
@@ -340,6 +316,10 @@ public GraphDatabaseService getRawGraph() {
return this.rawGraph;
}
+ public DatabaseManagementService getManagementService() {
+ return this.managementService;
+ }
+
public Features getFeatures() {
return FEATURES;
}
@@ -355,8 +335,8 @@ public GraphQuery query() {
public CloseableIterable