From 291f68dc362bf075ffd4f3469d90c8a11a22627c Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 14:22:37 +0200 Subject: [PATCH 1/8] GraphDatabase interface --- .../data/graph/core/GraphDatabase.java | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java new file mode 100644 index 000000000..96f8abe23 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java @@ -0,0 +1,59 @@ +package org.springframework.data.graph.core; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; + +import java.util.Map; + +/** + * @author mh + * @since 29.03.11 + */ +public interface GraphDatabase { + /** + * @return the reference node of the underlying graph database + */ + Node getReferenceNode(); + + /** + * @param id node id + * @return the requested node of the underlying graph database + * @throws org.neo4j.graphdb.NotFoundException + */ + Node getNode(long id); + + /** + * Transactionally creates the node, sets the properties (if any) and indexes the given fields (if any). + * Two shortcut means of providing the properties (very short with static imports) + * graphDatabase.createNode(PropertyMap._("name","value")); + * graphDatabase.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * @param props properties to be set at node creation might be null + * @param indexFields fields that are automatically indexed from the given properties for the newly created ndoe + * @return the newly created node + */ + Node createNode(Map props, String... indexFields); + + /** + * Delegates to the GraphDatabaseService + * @param id relationship id + * @return the requested relationship of the underlying graph database + * @throws org.neo4j.graphdb.NotFoundException + */ + Relationship getRelationship(long id); + + /** + * Transactionally creates the relationship, sets the properties (if any) and indexes the given fielss (if any) + * Two shortcut means of providing the properties (very short with static imports) + * graphDatabase.createRelationship(from,to,TYPE, PropertyMap._("name","value")); + * graphDatabase.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * @param startNode start-node of relationship + * @param endNode end-node of relationship + * @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name") + * @param props optional initial properties + * @param indexFields optional indexed fields + * @return the newly created relationship + */ + Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields); + +} From ec035d5ccea3a700abbab9940b71698ec1ab9923 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 14:41:59 +0200 Subject: [PATCH 2/8] all necessary methods for GraphDatabase --- .../data/graph/core/GraphDatabase.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java index 96f8abe23..be4d85185 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java @@ -1,8 +1,10 @@ package org.springframework.data.graph.core; import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.index.Index; import java.util.Map; @@ -35,7 +37,6 @@ public interface GraphDatabase { Node createNode(Map props, String... indexFields); /** - * Delegates to the GraphDatabaseService * @param id relationship id * @return the requested relationship of the underlying graph database * @throws org.neo4j.graphdb.NotFoundException @@ -56,4 +57,19 @@ public interface GraphDatabase { */ Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields); + /** + * @param indexName existing index name, not null + * @return existing index {@link Index} + * @throws IllegalArgumentException if the index doesn't exist + */ + Index getIndex(String indexName); + + /** + * creates a index + * @param type type of index requested - either Node.class or Relationship.class + * @param indexName, not null + * @param fullText true if a fulltext queryable index is needed, false for exact match + * @return node index {@link Index} + */ + Index createIndex(Class type, String indexName, boolean fullText); } From 704163eeaf28b449e662fca65777054a259e1af3 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 15:39:21 +0200 Subject: [PATCH 3/8] all necessary methods for GraphDatabase GraphDatabaseFactory and tests for that changed RestGraphDatabase to implement GraphDatabase --- spring-data-neo4j-rest/pom.xml | 1 - .../java/org/neo4j/kernel/RestConfig.java | 133 ------------------ .../rest/graphdb/GraphDatabaseFactory.java | 39 ----- .../neo4j/rest/graphdb/RestGraphDatabase.java | 126 ++++++----------- .../data/graph/core/GraphDatabase.java | 7 + .../data/graph/core/GraphDatabaseFactory.java | 88 ++++++++++++ 6 files changed, 138 insertions(+), 256 deletions(-) delete mode 100644 spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java delete mode 100644 spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/GraphDatabaseFactory.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java diff --git a/spring-data-neo4j-rest/pom.xml b/spring-data-neo4j-rest/pom.xml index a16d22487..82ef9d30a 100644 --- a/spring-data-neo4j-rest/pom.xml +++ b/spring-data-neo4j-rest/pom.xml @@ -42,7 +42,6 @@ org.springframework.data spring-data-neo4j 1.0.0.BUILD-SNAPSHOT - test org.hibernate diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java deleted file mode 100644 index 87a15ac0b..000000000 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.neo4j.kernel; - -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.kernel.Config; -import org.neo4j.kernel.IdGeneratorFactory; -import org.neo4j.kernel.impl.core.*; -import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction; -import org.neo4j.kernel.impl.nioneo.store.StoreId; -import org.neo4j.kernel.impl.transaction.LockManager; -import org.neo4j.kernel.impl.transaction.TxModule; -import org.neo4j.kernel.impl.transaction.xaframework.LogBufferFactory; -import org.neo4j.kernel.impl.transaction.xaframework.TxIdGenerator; -import org.neo4j.rest.graphdb.RestGraphDatabase; - -import javax.transaction.*; -import javax.transaction.xa.XAResource; -import java.util.Collections; -import java.util.Map; - -/** -* @author mh -* @since 23.02.11 -*/ -public class RestConfig extends Config { - public RestConfig(GraphDatabaseService graphDb, String storeDir, StoreId storeId, - Map inputParams, KernelPanicEventGenerator kpe, - TxModule txModule, LockManager lockManager, LockReleaser lockReleaser, - IdGeneratorFactory idGeneratorFactory, - TxEventSyncHookFactory txSyncHookFactory, RelationshipTypeCreator relTypeCreator, - TxIdGenerator txIdGenerator, LastCommittedTxIdSetter lastCommittedTxIdSetter, - FileSystemAbstraction fileSystem, LogBufferFactory logBufferFactory) { - super(graphDb, storeDir, storeId, - inputParams, kpe, txModule, lockManager, lockReleaser, idGeneratorFactory, txSyncHookFactory, relTypeCreator, txIdGenerator, lastCommittedTxIdSetter, fileSystem, logBufferFactory); - } - - public RestConfig(RestGraphDatabase restGraphDatabase) { - super(restGraphDatabase, restGraphDatabase.getStoreDir(), null, - Collections.emptyMap(),null, - new TxModule(true,null){ - @Override - public TransactionManager getTxManager() { - return new NullTransactionManager(); - } - }, - null,null, - null,null,null,null,null, - null,null); - } - - private static class NullTransactionManager implements TransactionManager { - private static final Transaction TRANSACTION = new Transaction() { - @Override - public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException { - - } - - @Override - public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException { - return false; - } - - @Override - public boolean enlistResource(XAResource xaResource) throws IllegalStateException, RollbackException, SystemException { - return false; - } - - @Override - public int getStatus() throws SystemException { - return Status.STATUS_NO_TRANSACTION; - } - - @Override - public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException, SystemException { - - } - - @Override - public void rollback() throws IllegalStateException, SystemException { - - } - - @Override - public void setRollbackOnly() throws IllegalStateException, SystemException { - - } - }; - - @Override - public void begin() throws NotSupportedException, SystemException { - - } - - @Override - public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, SystemException { - - } - - @Override - public int getStatus() throws SystemException { - return 0; - } - - @Override - public Transaction getTransaction() throws SystemException { - return TRANSACTION; - } - - @Override - public void resume(Transaction transaction) throws IllegalStateException, InvalidTransactionException, SystemException { - - } - - @Override - public void rollback() throws IllegalStateException, SecurityException, SystemException { - - } - - @Override - public void setRollbackOnly() throws IllegalStateException, SystemException { - - } - - @Override - public void setTransactionTimeout(int i) throws SystemException { - - } - - @Override - public Transaction suspend() throws SystemException { - return TRANSACTION; - } - } -} diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/GraphDatabaseFactory.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/GraphDatabaseFactory.java deleted file mode 100644 index 497c61737..000000000 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/GraphDatabaseFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.neo4j.rest.graphdb; - -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.kernel.EmbeddedGraphDatabase; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; - -/** - * @author mh - * @since 25.01.11 - */ -public class GraphDatabaseFactory { - public static GraphDatabaseService databaseFor(String url) { - return databaseFor( url, null,null ); - } - - public static GraphDatabaseService databaseFor(String url, String username, String password) { - if (url.startsWith( "http://" ) || url.startsWith( "https://" )) { - return new RestGraphDatabase( toURI( url ), username,password ); - } - String path=url; - if (url.startsWith( "file:" )) { - path = toURI( url ).getPath(); - } - File file = new File( path ); - if (!file.isDirectory()) file=file.getParentFile(); - return new EmbeddedGraphDatabase( file.getAbsolutePath() ); - } - - private static URI toURI( String uri ) { - try { - return new URI(uri); - } catch ( URISyntaxException e ) { - throw new RuntimeException( "Error using URI "+uri, e); - } - } -} diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java index dc11bcbfa..ac96d8a63 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java @@ -4,18 +4,21 @@ import com.sun.jersey.api.client.ClientResponse; import org.neo4j.graphdb.*; import org.neo4j.graphdb.event.KernelEventHandler; import org.neo4j.graphdb.event.TransactionEventHandler; +import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.index.IndexManager; +import org.neo4j.graphdb.traversal.TraversalDescription; import org.neo4j.kernel.AbstractGraphDatabase; import org.neo4j.kernel.Config; import org.neo4j.kernel.RestConfig; import org.neo4j.rest.graphdb.index.RestIndexManager; +import org.springframework.data.graph.core.GraphDatabase; import javax.ws.rs.core.Response.Status; import java.io.Serializable; import java.net.URI; import java.util.Map; -public class RestGraphDatabase extends AbstractGraphDatabase { +public class RestGraphDatabase implements GraphDatabase { private RestRequest restRequest; private long propertyRefetchTimeInMillis = 1000; @@ -28,74 +31,26 @@ public class RestGraphDatabase extends AbstractGraphDatabase { restRequest = new RestRequest( uri, user, password ); } - public Transaction beginTx() { - return new Transaction() { - public void success() { - } - - public void finish() { - - } - - public void failure() { - } - }; + @Override + public Node getNode(long id) { + ClientResponse response = restRequest.get("node/" + id); + if ( restRequest.statusIs(response, Status.NOT_FOUND) ) { + throw new NotFoundException( "" + id ); + } + return new RestNode( restRequest.toMap(response), this ); } - public TransactionEventHandler registerTransactionEventHandler( TransactionEventHandler tTransactionEventHandler ) { - throw new UnsupportedOperationException(); - } - - public TransactionEventHandler unregisterTransactionEventHandler( TransactionEventHandler tTransactionEventHandler ) { - throw new UnsupportedOperationException(); - } - - public KernelEventHandler registerKernelEventHandler( KernelEventHandler kernelEventHandler ) { - throw new UnsupportedOperationException(); - } - - public KernelEventHandler unregisterKernelEventHandler( KernelEventHandler kernelEventHandler ) { - throw new UnsupportedOperationException(); - } - - public IndexManager index() { - return new RestIndexManager( restRequest, this ); - } - - public Node createNode() { - ClientResponse response = restRequest.post( "node", null ); + @Override + public Node createNode(Map props, String... indexFields) { + ClientResponse response = restRequest.post("node", null); if ( restRequest.statusOtherThan( response, Status.CREATED ) ) { throw new RuntimeException( "" + response.getStatus() ); } return new RestNode( response.getLocation(), this ); } - public boolean enableRemoteShell() { - throw new UnsupportedOperationException(); - } - - public boolean enableRemoteShell( Map config ) { - throw new UnsupportedOperationException(); - } - - public Iterable getAllNodes() { - throw new UnsupportedOperationException(); - } - - public Node getNodeById( long id ) { - ClientResponse response = restRequest.get( "node/" + id ); - if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) { - throw new NotFoundException( "" + id ); - } - return new RestNode( restRequest.toMap( response ), this ); - } - - public Node getReferenceNode() { - Map map = restRequest.toMap( restRequest.get( "" ) ); - return new RestNode( (String) map.get( "reference_node" ), this ); - } - - public Relationship getRelationshipById( long id ) { + @Override + public Relationship getRelationship(long id) { ClientResponse response = restRequest.get( "relationship/" + id ); if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) { throw new NotFoundException( "" + id ); @@ -103,11 +58,35 @@ public class RestGraphDatabase extends AbstractGraphDatabase { return new RestRelationship( restRequest.toMap( response ), this ); } - public Iterable getRelationshipTypes() { - throw new UnsupportedOperationException(); + @Override + public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields) { + Relationship relationship = startNode.createRelationshipTo(endNode, type); + return relationship; } - public void shutdown() { + @Override + public Index getIndex(String indexName) { + return (Index) index().forNodes(indexName); // todo + } + + @Override + public Index createIndex(Class type, String indexName, boolean fullText) { + return (Index) index().forNodes(indexName); // todo + } + + @Override + public TraversalDescription createTraversalDescription() { + return new RestTraversal(); + } + + private RestIndexManager index() { + return new RestIndexManager( restRequest, this ); + } + + @Override + public Node getReferenceNode() { + Map map = restRequest.toMap( restRequest.get( "" ) ); + return new RestNode( (String) map.get( "reference_node" ), this ); } public RestRequest getRestRequest() { @@ -117,23 +96,4 @@ public class RestGraphDatabase extends AbstractGraphDatabase { public long getPropertyRefetchTimeInMillis() { return propertyRefetchTimeInMillis; } - @Override - public String getStoreDir() { - return restRequest.getUri().toString(); - } - - @Override - public Config getConfig() { - return new RestConfig(this); - } - - @Override - public T getManagementBean(Class type) { - return null; - } - - @Override - public boolean isReadOnly() { - return false; - } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java index be4d85185..81e71a67e 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java @@ -5,6 +5,7 @@ import org.neo4j.graphdb.PropertyContainer; import org.neo4j.graphdb.Relationship; import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.index.Index; +import org.neo4j.graphdb.traversal.TraversalDescription; import java.util.Map; @@ -72,4 +73,10 @@ public interface GraphDatabase { * @return node index {@link Index} */ Index createIndex(Class type, String indexName, boolean fullText); + + + /** + * @return a TraversalDescription as starting point for defining a traversal + */ + TraversalDescription createTraversalDescription(); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java new file mode 100644 index 000000000..166656aee --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java @@ -0,0 +1,88 @@ +package org.springframework.data.graph.core; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.data.graph.neo4j.support.LocalGraphDatabase; + +import javax.annotation.PreDestroy; +import java.io.File; +import java.lang.reflect.Constructor; +import java.net.URI; + +/** + * @author mh + * @since 25.01.11 + */ +public class GraphDatabaseFactory implements FactoryBean { + + private String storeLocation; + private String userName; + private String password; + protected GraphDatabase graphDatabase; + + public String getStoreLocation() { + return storeLocation; + } + + public void setStoreLocation(String storeLocation) { + this.storeLocation = storeLocation; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + private GraphDatabase databaseFor(String url, String username, String password) throws Exception { + if (url.startsWith( "http://" ) || url.startsWith( "https://" )) { + return createRestGraphDatabase(url, username, password); + } + String path=url; + if (url.startsWith( "file:" )) { + path = new URI(url).getPath(); + } + File file = new File( path ); + // if (!file.isDirectory()) file=file.getParentFile(); + return new LocalGraphDatabase(file); + } + + private GraphDatabase createRestGraphDatabase(String url, String username, String password) throws Exception { + Class restGraphDatabaseClass = Class.forName("org.neo4j.rest.graphdb.RestGraphDatabase"); + Constructor constructor = restGraphDatabaseClass.getConstructor(URI.class, String.class, String.class); + return (GraphDatabase) constructor.newInstance(new URI(url), username,password ); + } + + @Override + public GraphDatabase getObject() throws Exception { + if (graphDatabase==null) graphDatabase = databaseFor(storeLocation, userName, password); + return graphDatabase; + } + + @PreDestroy + public void shutdown() { + if (graphDatabase instanceof LocalGraphDatabase) { + ((LocalGraphDatabase)graphDatabase).shutdown(); + } + } + + @Override + public Class getObjectType() { + return GraphDatabaseService.class; + } + + @Override + public boolean isSingleton() { + return true; + } +} From ef2cdb6c71a320f35fede782af2aba96fb4fb253 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 15:47:42 +0200 Subject: [PATCH 4/8] PropertyContainer returns GraphDatabaseService which is bad as it breaks the assumption let it throw UnsupportedOperationException() there is a second getRestGraphDatabase() method for returning the actual RestGraphDatabase --- .../src/main/java/org/neo4j/rest/graphdb/RestEntity.java | 7 ++++++- .../java/org/neo4j/rest/graphdb/RestGraphDatabase.java | 7 ------- .../src/main/java/org/neo4j/rest/graphdb/RestNode.java | 4 ++-- .../java/org/neo4j/rest/graphdb/RestRelationship.java | 2 +- .../test/java/org/neo4j/rest/graphdb/RestTestBase.java | 9 ++------- 5 files changed, 11 insertions(+), 18 deletions(-) diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestEntity.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestEntity.java index 2b9c201a9..aa06cb93a 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestEntity.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestEntity.java @@ -1,6 +1,7 @@ package org.neo4j.rest.graphdb; import com.sun.jersey.api.client.ClientResponse; +import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.NotFoundException; import org.neo4j.graphdb.PropertyContainer; import org.neo4j.helpers.collection.IterableWrapper; @@ -154,7 +155,11 @@ public class RestEntity implements PropertyContainer { return getClass().equals( o.getClass() ) && getId() == ( (RestEntity) o ).getId(); } - public RestGraphDatabase getGraphDatabase() { + public GraphDatabaseService getGraphDatabase() { + throw new UnsupportedOperationException("No GraphDatabaseService semantics for the REST-API"); + } + + public RestGraphDatabase getRestGraphDatabase() { return graphDatabase; } diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java index ac96d8a63..69345e013 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java @@ -2,19 +2,12 @@ package org.neo4j.rest.graphdb; import com.sun.jersey.api.client.ClientResponse; import org.neo4j.graphdb.*; -import org.neo4j.graphdb.event.KernelEventHandler; -import org.neo4j.graphdb.event.TransactionEventHandler; import org.neo4j.graphdb.index.Index; -import org.neo4j.graphdb.index.IndexManager; import org.neo4j.graphdb.traversal.TraversalDescription; -import org.neo4j.kernel.AbstractGraphDatabase; -import org.neo4j.kernel.Config; -import org.neo4j.kernel.RestConfig; import org.neo4j.rest.graphdb.index.RestIndexManager; import org.springframework.data.graph.core.GraphDatabase; import javax.ws.rs.core.Response.Status; -import java.io.Serializable; import java.net.URI; import java.util.Map; diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestNode.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestNode.java index 27bdb2a60..b0870e38e 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestNode.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestNode.java @@ -35,7 +35,7 @@ public class RestNode extends RestEntity implements Node { if ( restRequest.statusOtherThan( response, Status.CREATED ) ) { throw new RuntimeException( "" + response.getStatus() ); } - return new RestRelationship( response.getLocation(), getGraphDatabase() ); + return new RestRelationship( response.getLocation(), getRestGraphDatabase() ); } public Iterable getRelationships() { @@ -48,7 +48,7 @@ public class RestNode extends RestEntity implements Node { (Collection) restRequest.toEntity( response ) ) { @Override protected Relationship underlyingObjectToObject( Object data ) { - return new RestRelationship( (Map) data, getGraphDatabase() ); + return new RestRelationship( (Map) data, getRestGraphDatabase() ); } }; } diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestRelationship.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestRelationship.java index 1ec933624..e81600200 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestRelationship.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestRelationship.java @@ -44,7 +44,7 @@ public class RestRelationship extends RestEntity implements Relationship { } private RestNode node( String uri ) { - return new RestNode( uri, getGraphDatabase() ); + return new RestNode( uri, getRestGraphDatabase() ); } public Node getStartNode() { diff --git a/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java b/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java index 03851bc5f..b1e04a5bc 100644 --- a/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java +++ b/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java @@ -25,7 +25,7 @@ import java.util.Iterator; public class RestTestBase { - protected GraphDatabaseService graphDb; + protected RestGraphDatabase graphDb; private static final String HOSTNAME = "localhost"; private static final int PORT = 7473; private static LocalTestServer neoServer = new LocalTestServer(HOSTNAME,PORT).withPropertiesFile("test-db.properties"); @@ -47,11 +47,6 @@ public class RestTestBase { neoServer.cleanDb(); } - @After - public void tearDown() throws Exception { - graphDb.shutdown(); - } - @AfterClass public static void shutdownDb() { neoServer.stop(); @@ -61,7 +56,7 @@ public class RestTestBase { protected Relationship relationship() { Iterator it = node().getRelationships(Direction.OUTGOING).iterator(); if (it.hasNext()) return it.next(); - return node().createRelationshipTo(graphDb.createNode(), Type.TEST); + return node().createRelationshipTo(graphDb.createNode(null), Type.TEST); } protected Node node() { From 3348eec4e6ef1c633446d38985863c61af240cad Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 22:04:32 +0200 Subject: [PATCH 5/8] Test for GraphDatabaseFactory --- .../graph/core/GraphDatabaseFactoryTest.java | 43 +++++++++++++++++++ .../GraphDatabaseFactory-context.xml | 24 +++++++++++ 2 files changed, 67 insertions(+) create mode 100644 spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java create mode 100644 spring-data-neo4j/src/test/resources/GraphDatabaseFactory-context.xml diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java new file mode 100644 index 000000000..98278f4a8 --- /dev/null +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java @@ -0,0 +1,43 @@ +package org.springframework.data.graph.core; + +import org.junit.Test; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.data.graph.neo4j.support.LocalGraphDatabase; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.hamcrest.core.IsNot.not; +import static org.hamcrest.core.IsNull.nullValue; +import static org.junit.Assert.assertThat; + +/** + * @author mh + * @since 29.03.11 + */ +public class GraphDatabaseFactoryTest { + + @Test + public void shouldCreateLocalDatabaseFromContext() throws Exception { + ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("GraphDatabaseFactory-context.xml"); + try { + GraphDatabase graphDatabase = ctx.getBean("graphDatabase", GraphDatabase.class); + assertThat(graphDatabase, is(not(nullValue()))); + assertThat(graphDatabase, is(instanceOf(LocalGraphDatabase.class))); + } finally { + ctx.close(); + } + + } + @Test + public void shouldCreateLocalDatabase() throws Exception { + GraphDatabaseFactory factory = new GraphDatabaseFactory(); + try { + factory.setStoreLocation("target/test-db"); + GraphDatabase graphDatabase = factory.getObject(); + assertThat(graphDatabase, is(not(nullValue()))); + assertThat(graphDatabase,is(instanceOf(LocalGraphDatabase.class))); + } finally { + factory.shutdown(); + } + } +} diff --git a/spring-data-neo4j/src/test/resources/GraphDatabaseFactory-context.xml b/spring-data-neo4j/src/test/resources/GraphDatabaseFactory-context.xml new file mode 100644 index 000000000..e1863ed64 --- /dev/null +++ b/spring-data-neo4j/src/test/resources/GraphDatabaseFactory-context.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + \ No newline at end of file From eaa5b4a672653706d376e7a323e597b20b32cda2 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Tue, 29 Mar 2011 22:05:14 +0200 Subject: [PATCH 6/8] Implementation of GraphDatabase for EmbeddedGD --- .../neo4j/support/LocalGraphDatabase.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java new file mode 100644 index 000000000..c61af0fa1 --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java @@ -0,0 +1,83 @@ +package org.springframework.data.graph.neo4j.support; + +import org.neo4j.graphdb.Node; +import org.neo4j.graphdb.PropertyContainer; +import org.neo4j.graphdb.Relationship; +import org.neo4j.graphdb.RelationshipType; +import org.neo4j.graphdb.index.Index; +import org.neo4j.graphdb.index.IndexManager; +import org.neo4j.graphdb.traversal.TraversalDescription; +import org.neo4j.index.impl.lucene.LuceneIndexImplementation; +import org.neo4j.kernel.EmbeddedGraphDatabase; +import org.neo4j.kernel.Traversal; +import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.core.NodeBacked; +import org.springframework.data.graph.core.RelationshipBacked; + +import java.io.File; +import java.util.Map; + +/** + * @author mh + * @since 29.03.11 + */ +public class LocalGraphDatabase implements GraphDatabase { + + protected EmbeddedGraphDatabase delegate; + + public LocalGraphDatabase(File file) { + delegate = new EmbeddedGraphDatabase(file.getAbsolutePath()); + } + + @Override + public Node getReferenceNode() { + return delegate.getReferenceNode(); + } + + @Override + public Node getNode(long id) { + return delegate.getNodeById(id); + } + + @Override + public Node createNode(Map props, String... indexFields) { + return null; + } + + @Override + public Relationship getRelationship(long id) { + return delegate.getRelationshipById(id); + } + + @Override + public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields) { + return null; + } + + @Override + public Index getIndex(String indexName) { + IndexManager indexManager = delegate.index(); + if (indexManager.existsForNodes(indexName)) return (Index) indexManager.forNodes(indexName); + if (indexManager.existsForRelationships(indexName)) return (Index) indexManager.forRelationships(indexName); + throw new IllegalArgumentException("Index "+indexName+" does not exist."); + } + + // TODO handle existing indexes + @Override + public Index createIndex(Class type, String indexName, boolean fullText) { + IndexManager indexManager = delegate.index(); + Map config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG; + if (NodeBacked.class.isAssignableFrom(type)) return (Index) indexManager.forNodes(indexName, config); + if (RelationshipBacked.class.isAssignableFrom(type)) return (Index) indexManager.forRelationships(indexName, config); + throw new IllegalArgumentException("Wrong index type supplied "+type); + } + + @Override + public TraversalDescription createTraversalDescription() { + return Traversal.description(); + } + + public void shutdown() { + delegate.shutdown(); + } +} From d606d319fa24d5035c90e86469ea5466533a119a Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Thu, 31 Mar 2011 14:08:26 +0200 Subject: [PATCH 7/8] GraphDatabase for RestGraphDatabase --- .../java/org/neo4j/kernel/RestConfig.java | 131 ++++++++++++++++++ .../data/graph/core/Property.java | 48 +++++++ .../support/DelegatingGraphDatabase.java | 110 +++++++++++++++ .../neo4j/support/LocalGraphDatabase.java | 83 ----------- 4 files changed, 289 insertions(+), 83 deletions(-) create mode 100644 spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/core/Property.java create mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java delete mode 100644 spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java new file mode 100644 index 000000000..e8008799a --- /dev/null +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/kernel/RestConfig.java @@ -0,0 +1,131 @@ +package org.neo4j.kernel; + +import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.kernel.impl.core.*; +import org.neo4j.kernel.impl.nioneo.store.FileSystemAbstraction; +import org.neo4j.kernel.impl.nioneo.store.StoreId; +import org.neo4j.kernel.impl.transaction.LockManager; +import org.neo4j.kernel.impl.transaction.TxModule; +import org.neo4j.kernel.impl.transaction.xaframework.LogBufferFactory; +import org.neo4j.kernel.impl.transaction.xaframework.TxIdGenerator; +import org.neo4j.rest.graphdb.RestGraphDatabase; + +import javax.transaction.*; +import javax.transaction.xa.XAResource; +import java.util.Collections; +import java.util.Map; + +/** +* @author mh +* @since 23.02.11 +*/ +public class RestConfig extends Config { + public RestConfig(GraphDatabaseService graphDb, String storeDir, StoreId storeId, + Map inputParams, KernelPanicEventGenerator kpe, + TxModule txModule, LockManager lockManager, LockReleaser lockReleaser, + IdGeneratorFactory idGeneratorFactory, + TxEventSyncHookFactory txSyncHookFactory, RelationshipTypeCreator relTypeCreator, + TxIdGenerator txIdGenerator, LastCommittedTxIdSetter lastCommittedTxIdSetter, + FileSystemAbstraction fileSystem, LogBufferFactory logBufferFactory) { + super(graphDb, storeDir, storeId, + inputParams, kpe, txModule, lockManager, lockReleaser, idGeneratorFactory, txSyncHookFactory, relTypeCreator, txIdGenerator, lastCommittedTxIdSetter, fileSystem, logBufferFactory); + } + + public RestConfig(RestGraphDatabase restGraphDatabase) { + super(restGraphDatabase, restGraphDatabase.getStoreDir(), null, + Collections.emptyMap(),null, + new TxModule(true,null){ + @Override + public TransactionManager getTxManager() { + return new NullTransactionManager(); + } + }, + null,null, + null,null,null,null,null, + null,null); + } + + private static class NullTransactionManager implements TransactionManager { + private static final Transaction NULL_JAVA_TRANSACTION = new Transaction() { + @Override + public void commit() throws HeuristicMixedException, HeuristicRollbackException, RollbackException, SecurityException, SystemException { + + } + + @Override + public boolean delistResource(XAResource xaResource, int i) throws IllegalStateException, SystemException { + return false; + } + + @Override + public boolean enlistResource(XAResource xaResource) throws IllegalStateException, RollbackException, SystemException { + return false; + } + + @Override + public int getStatus() throws SystemException { + return Status.STATUS_NO_TRANSACTION; + } + + @Override + public void registerSynchronization(Synchronization synchronization) throws IllegalStateException, RollbackException, SystemException { + + } + + @Override + public void rollback() throws IllegalStateException, SystemException { + + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + + } + }; + + @Override + public void begin() throws NotSupportedException, SystemException { + + } + + @Override + public void commit() throws HeuristicMixedException, HeuristicRollbackException, IllegalStateException, RollbackException, SecurityException, SystemException { + + } + + @Override + public int getStatus() throws SystemException { + return 0; + } + + @Override + public Transaction getTransaction() throws SystemException { + return NULL_JAVA_TRANSACTION; + } + + @Override + public void resume(Transaction transaction) throws IllegalStateException, InvalidTransactionException, SystemException { + + } + + @Override + public void rollback() throws IllegalStateException, SecurityException, SystemException { + + } + + @Override + public void setRollbackOnly() throws IllegalStateException, SystemException { + + } + + @Override + public void setTransactionTimeout(int i) throws SystemException { + + } + + @Override + public Transaction suspend() throws SystemException { + return NULL_JAVA_TRANSACTION; + } + } +} \ No newline at end of file diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/Property.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/Property.java new file mode 100644 index 000000000..06f0de6af --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/Property.java @@ -0,0 +1,48 @@ +package org.springframework.data.graph.core; + +import java.util.Map; + +/** + * @author mh + * @since 31.03.11 + */ +public class Property { + public final String name; + public final Object value; + + public Property(String name, Object value) { + this.name = name; + this.value = value; + } + + public static Property _(String name, Object value) { + return new Property(name, value); + } + + public static Property[] _(String name1, Object value1, String name2, Object value2) { + return new Property[]{new Property(name1, value1), new Property(name2, value2)}; + } + + public static Property[] _(String name1, Object value1, String name2, Object value2, String name3, Object value3) { + return new Property[]{new Property(name1, value1), new Property(name2, value2), new Property(name3, value3)}; + } + + public static Property[] _(Object... nameValuePairs) { + if (nameValuePairs.length % 2 != 0) + throw new IllegalArgumentException("there must be an even number of name value pairs"); + Property[] result = new Property[nameValuePairs.length / 2]; + for (int i = 0; i < result.length; i++) { + result[i] = new Property(nameValuePairs[i * 2].toString(), nameValuePairs[i * 2 + 1]); + } + return result; + } + + public static Property[] _(Map properties) { + Property[] result = new Property[properties.size()]; + int i = 0; + for (Map.Entry entry : properties.entrySet()) { + result[i++] = new Property(entry.getKey(), entry.getValue()); + } + return result; + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java new file mode 100644 index 000000000..e2d9a4e4d --- /dev/null +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java @@ -0,0 +1,110 @@ +package org.springframework.data.graph.neo4j.support; + +import org.neo4j.graphdb.*; +import org.neo4j.graphdb.index.Index; +import org.neo4j.graphdb.index.IndexManager; +import org.neo4j.graphdb.traversal.TraversalDescription; +import org.neo4j.index.impl.lucene.LuceneIndexImplementation; +import org.neo4j.kernel.AbstractGraphDatabase; +import org.neo4j.kernel.Traversal; +import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.core.NodeBacked; +import org.springframework.data.graph.core.Property; +import org.springframework.data.graph.core.RelationshipBacked; + +import java.util.Map; + +/** + * @author mh + * @since 29.03.11 + */ +public class DelegatingGraphDatabase implements GraphDatabase { + + protected AbstractGraphDatabase delegate; + + public DelegatingGraphDatabase(final AbstractGraphDatabase delegate) { + this.delegate = delegate; + } + + @Override + public Node getReferenceNode() { + return delegate.getReferenceNode(); + } + + @Override + public Node getNodeById(long id) { + return delegate.getNodeById(id); + } + + @Override + public Node createNode(Property... props) { + return setProperties(delegate.createNode(), props); + } + + private T setProperties(T element, Property[] props) { + if (props==null || props.length==0) return element; + for (Property prop : props) { + element.setProperty(prop.name, prop.value); + } + return element; + } + + @Override + public Relationship getRelationshipById(long id) { + return delegate.getRelationshipById(id); + } + + @Override + public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) { + return setProperties(startNode.createRelationshipTo(endNode,type),props); + } + + @Override + public Index getIndex(String indexName) { + IndexManager indexManager = delegate.index(); + if (indexManager.existsForNodes(indexName)) return (Index) indexManager.forNodes(indexName); + if (indexManager.existsForRelationships(indexName)) return (Index) indexManager.forRelationships(indexName); + throw new IllegalArgumentException("Index "+indexName+" does not exist."); + } + + // TODO handle existing indexes + @Override + public Index createIndex(Class type, String indexName, boolean fullText) { + IndexManager indexManager = delegate.index(); + if (isNode(type)) { + if (indexManager.existsForNodes(indexName)) + return (Index) checkAndGetExistingIndex(indexName, fullText, indexManager.forNodes(indexName)); + return (Index) indexManager.forNodes(indexName, indexConfigFor(fullText)); + } else { + if (indexManager.existsForRelationships(indexName)) + return (Index) checkAndGetExistingIndex(indexName, fullText, indexManager.forRelationships(indexName)); + return (Index) indexManager.forRelationships(indexName, indexConfigFor(fullText)); + } + } + + public boolean isNode(Class type) { + if (type.equals(Node.class)) return true; + if (type.equals(Relationship.class)) return false; + throw new IllegalArgumentException("Unknown Graph Primitive, neither Node nor Relationship"+type); + } + + private Index checkAndGetExistingIndex(final String indexName, boolean fullText, final Index index) { + Map existingConfig = delegate.index().getConfiguration(index); + Map config = indexConfigFor(fullText); + if (config.equals(existingConfig)) return index; + throw new IllegalArgumentException("Setup for index "+indexName+" does not match "+(fullText ? "fulltext":"exact")); + } + + private Map indexConfigFor(boolean fullText) { + return fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG; + } + + @Override + public TraversalDescription createTraversalDescription() { + return Traversal.description(); + } + + public void shutdown() { + delegate.shutdown(); + } +} diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java deleted file mode 100644 index c61af0fa1..000000000 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/LocalGraphDatabase.java +++ /dev/null @@ -1,83 +0,0 @@ -package org.springframework.data.graph.neo4j.support; - -import org.neo4j.graphdb.Node; -import org.neo4j.graphdb.PropertyContainer; -import org.neo4j.graphdb.Relationship; -import org.neo4j.graphdb.RelationshipType; -import org.neo4j.graphdb.index.Index; -import org.neo4j.graphdb.index.IndexManager; -import org.neo4j.graphdb.traversal.TraversalDescription; -import org.neo4j.index.impl.lucene.LuceneIndexImplementation; -import org.neo4j.kernel.EmbeddedGraphDatabase; -import org.neo4j.kernel.Traversal; -import org.springframework.data.graph.core.GraphDatabase; -import org.springframework.data.graph.core.NodeBacked; -import org.springframework.data.graph.core.RelationshipBacked; - -import java.io.File; -import java.util.Map; - -/** - * @author mh - * @since 29.03.11 - */ -public class LocalGraphDatabase implements GraphDatabase { - - protected EmbeddedGraphDatabase delegate; - - public LocalGraphDatabase(File file) { - delegate = new EmbeddedGraphDatabase(file.getAbsolutePath()); - } - - @Override - public Node getReferenceNode() { - return delegate.getReferenceNode(); - } - - @Override - public Node getNode(long id) { - return delegate.getNodeById(id); - } - - @Override - public Node createNode(Map props, String... indexFields) { - return null; - } - - @Override - public Relationship getRelationship(long id) { - return delegate.getRelationshipById(id); - } - - @Override - public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields) { - return null; - } - - @Override - public Index getIndex(String indexName) { - IndexManager indexManager = delegate.index(); - if (indexManager.existsForNodes(indexName)) return (Index) indexManager.forNodes(indexName); - if (indexManager.existsForRelationships(indexName)) return (Index) indexManager.forRelationships(indexName); - throw new IllegalArgumentException("Index "+indexName+" does not exist."); - } - - // TODO handle existing indexes - @Override - public Index createIndex(Class type, String indexName, boolean fullText) { - IndexManager indexManager = delegate.index(); - Map config = fullText ? LuceneIndexImplementation.FULLTEXT_CONFIG : LuceneIndexImplementation.EXACT_CONFIG; - if (NodeBacked.class.isAssignableFrom(type)) return (Index) indexManager.forNodes(indexName, config); - if (RelationshipBacked.class.isAssignableFrom(type)) return (Index) indexManager.forRelationships(indexName, config); - throw new IllegalArgumentException("Wrong index type supplied "+type); - } - - @Override - public TraversalDescription createTraversalDescription() { - return Traversal.description(); - } - - public void shutdown() { - delegate.shutdown(); - } -} From 2f37ea79f2c5f839fa612b37ab30e2156531dc12 Mon Sep 17 00:00:00 2001 From: Michael Hunger Date: Thu, 31 Mar 2011 14:08:48 +0200 Subject: [PATCH 8/8] GraphDatabase for RestGraphDatabase --- .../neo4j/rest/graphdb/RestGraphDatabase.java | 111 ++++++++++++++++-- .../org/neo4j/rest/graphdb/RestTraversal.java | 2 +- .../org/neo4j/rest/graphdb/RestTestBase.java | 10 -- .../data/graph/core/GraphDatabase.java | 18 +-- .../data/graph/core/GraphDatabaseFactory.java | 9 +- .../support/DelegatingGraphDatabase.java | 15 ++- .../graph/neo4j/template/Neo4jOperations.java | 21 ++-- .../graph/neo4j/template/Neo4jTemplate.java | 30 ++--- .../graph/core/GraphDatabaseFactoryTest.java | 6 +- .../neo4j/template/Neo4jTemplateApiTest.java | 5 +- .../neo4j/template/NeoTraversalTest.java | 2 +- 11 files changed, 151 insertions(+), 78 deletions(-) diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java index 69345e013..eaca8e9f2 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestGraphDatabase.java @@ -2,16 +2,24 @@ package org.neo4j.rest.graphdb; import com.sun.jersey.api.client.ClientResponse; import org.neo4j.graphdb.*; +import org.neo4j.graphdb.event.KernelEventHandler; +import org.neo4j.graphdb.event.TransactionEventHandler; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; +import org.neo4j.kernel.AbstractGraphDatabase; +import org.neo4j.kernel.Config; +import org.neo4j.kernel.RestConfig; import org.neo4j.rest.graphdb.index.RestIndexManager; import org.springframework.data.graph.core.GraphDatabase; +import org.springframework.data.graph.core.Property; import javax.ws.rs.core.Response.Status; +import java.io.Serializable; import java.net.URI; import java.util.Map; -public class RestGraphDatabase implements GraphDatabase { +public class RestGraphDatabase extends AbstractGraphDatabase implements GraphDatabase { + private RestRequest restRequest; private long propertyRefetchTimeInMillis = 1000; @@ -25,7 +33,7 @@ public class RestGraphDatabase implements GraphDatabase { } @Override - public Node getNode(long id) { + public Node getNodeById(long id) { ClientResponse response = restRequest.get("node/" + id); if ( restRequest.statusIs(response, Status.NOT_FOUND) ) { throw new NotFoundException( "" + id ); @@ -34,17 +42,17 @@ public class RestGraphDatabase implements GraphDatabase { } @Override - public Node createNode(Map props, String... indexFields) { + public Node createNode(Property... props) { ClientResponse response = restRequest.post("node", null); - if ( restRequest.statusOtherThan( response, Status.CREATED ) ) { + if ( restRequest.statusOtherThan(response, Status.CREATED) ) { throw new RuntimeException( "" + response.getStatus() ); } return new RestNode( response.getLocation(), this ); } @Override - public Relationship getRelationship(long id) { - ClientResponse response = restRequest.get( "relationship/" + id ); + public Relationship getRelationshipById(long id) { + ClientResponse response = restRequest.get("relationship/" + id); if ( restRequest.statusIs( response, Status.NOT_FOUND ) ) { throw new NotFoundException( "" + id ); } @@ -52,7 +60,7 @@ public class RestGraphDatabase implements GraphDatabase { } @Override - public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields) { + public Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props) { Relationship relationship = startNode.createRelationshipTo(endNode, type); return relationship; } @@ -72,7 +80,7 @@ public class RestGraphDatabase implements GraphDatabase { return new RestTraversal(); } - private RestIndexManager index() { + public RestIndexManager index() { return new RestIndexManager( restRequest, this ); } @@ -89,4 +97,91 @@ public class RestGraphDatabase implements GraphDatabase { public long getPropertyRefetchTimeInMillis() { return propertyRefetchTimeInMillis; } + + @Override + public Node createNode() { + return createNode((Property[])null); + } + + @Override + public Iterable getAllNodes() { + throw new UnsupportedOperationException(); + } + + @Override + public Iterable getRelationshipTypes() { + throw new UnsupportedOperationException(); + } + + @Override + public void shutdown() { + + } + + @Override + public boolean enableRemoteShell() { + return false; + } + + @Override + public boolean enableRemoteShell(Map initialProperties) { + return false; + } + + @Override + public Transaction beginTx() { + return new Transaction() { + @Override + public void failure() { + } + + @Override + public void success() { + } + + @Override + public void finish() { + } + }; + } + + @Override + public TransactionEventHandler registerTransactionEventHandler(TransactionEventHandler handler) { + return handler; + } + + @Override + public TransactionEventHandler unregisterTransactionEventHandler(TransactionEventHandler handler) { + return handler; + } + + @Override + public KernelEventHandler registerKernelEventHandler(KernelEventHandler handler) { + return handler; + } + + @Override + public KernelEventHandler unregisterKernelEventHandler(KernelEventHandler handler) { + return handler; + } + + @Override + public String getStoreDir() { + return getRestRequest().getUri().toString(); + } + + @Override + public Config getConfig() { + return new RestConfig(this); + } + + @Override + public T getManagementBean(Class type) { + return null; + } + + @Override + public boolean isReadOnly() { + return false; + } } diff --git a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestTraversal.java b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestTraversal.java index ba2a734d1..d7598115a 100644 --- a/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestTraversal.java +++ b/spring-data-neo4j-rest/src/main/java/org/neo4j/rest/graphdb/RestTraversal.java @@ -207,7 +207,7 @@ public class RestTraversal implements RestTraversalDescription { throw new RuntimeException( String.format( "Unexpected traversal result, %s instead of collection", col != null ? col.getClass() : null ) ); } - return new RestTraverser( (Collection)col, restNode.getGraphDatabase() ); + return new RestTraverser( (Collection)col, restNode.getRestGraphDatabase() ); } public static RestTraversalDescription description() diff --git a/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java b/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java index b1e04a5bc..d737eb92f 100644 --- a/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java +++ b/spring-data-neo4j-rest/src/test/java/org/neo4j/rest/graphdb/RestTestBase.java @@ -1,23 +1,13 @@ package org.neo4j.rest.graphdb; -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.ClientResponse; import org.apache.log4j.BasicConfigurator; -import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.neo4j.graphdb.Direction; -import org.neo4j.graphdb.GraphDatabaseService; import org.neo4j.graphdb.Node; import org.neo4j.graphdb.Relationship; -import org.neo4j.server.AddressResolver; -import org.neo4j.server.NeoServerWithEmbeddedWebServer; -import org.neo4j.server.modules.RESTApiModule; -import org.neo4j.server.modules.ThirdPartyJAXRSModule; -import org.neo4j.server.startup.healthcheck.StartupHealthCheck; -import org.neo4j.server.web.Jetty6WebServer; import java.net.URI; import java.net.URISyntaxException; diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java index 81e71a67e..d73b2ec94 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabase.java @@ -7,8 +7,6 @@ import org.neo4j.graphdb.RelationshipType; import org.neo4j.graphdb.index.Index; import org.neo4j.graphdb.traversal.TraversalDescription; -import java.util.Map; - /** * @author mh * @since 29.03.11 @@ -24,39 +22,40 @@ public interface GraphDatabase { * @return the requested node of the underlying graph database * @throws org.neo4j.graphdb.NotFoundException */ - Node getNode(long id); + Node getNodeById(long id); /** - * Transactionally creates the node, sets the properties (if any) and indexes the given fields (if any). + * Transactionally creates the node, sets the properties (if any). * Two shortcut means of providing the properties (very short with static imports) * graphDatabase.createNode(PropertyMap._("name","value")); * graphDatabase.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * * @param props properties to be set at node creation might be null - * @param indexFields fields that are automatically indexed from the given properties for the newly created ndoe * @return the newly created node */ - Node createNode(Map props, String... indexFields); + Node createNode(Property... props); /** * @param id relationship id * @return the requested relationship of the underlying graph database * @throws org.neo4j.graphdb.NotFoundException */ - Relationship getRelationship(long id); + Relationship getRelationshipById(long id); /** * Transactionally creates the relationship, sets the properties (if any) and indexes the given fielss (if any) * Two shortcut means of providing the properties (very short with static imports) * graphDatabase.createRelationship(from,to,TYPE, PropertyMap._("name","value")); * graphDatabase.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * + * * @param startNode start-node of relationship * @param endNode end-node of relationship * @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name") * @param props optional initial properties - * @param indexFields optional indexed fields * @return the newly created relationship */ - Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields); + Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props); /** * @param indexName existing index name, not null @@ -79,4 +78,5 @@ public interface GraphDatabase { * @return a TraversalDescription as starting point for defining a traversal */ TraversalDescription createTraversalDescription(); + } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java index 166656aee..f2d3a6740 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/core/GraphDatabaseFactory.java @@ -1,8 +1,9 @@ package org.springframework.data.graph.core; import org.neo4j.graphdb.GraphDatabaseService; +import org.neo4j.kernel.EmbeddedGraphDatabase; import org.springframework.beans.factory.FactoryBean; -import org.springframework.data.graph.neo4j.support.LocalGraphDatabase; +import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase; import javax.annotation.PreDestroy; import java.io.File; @@ -54,7 +55,7 @@ public class GraphDatabaseFactory implements FactoryBean { } File file = new File( path ); // if (!file.isDirectory()) file=file.getParentFile(); - return new LocalGraphDatabase(file); + return new DelegatingGraphDatabase(new EmbeddedGraphDatabase(file.getAbsolutePath())); } private GraphDatabase createRestGraphDatabase(String url, String username, String password) throws Exception { @@ -71,8 +72,8 @@ public class GraphDatabaseFactory implements FactoryBean { @PreDestroy public void shutdown() { - if (graphDatabase instanceof LocalGraphDatabase) { - ((LocalGraphDatabase)graphDatabase).shutdown(); + if (graphDatabase instanceof DelegatingGraphDatabase) { + ((DelegatingGraphDatabase)graphDatabase).shutdown(); } } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java index e2d9a4e4d..9ac73e436 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/support/DelegatingGraphDatabase.java @@ -41,12 +41,17 @@ public class DelegatingGraphDatabase implements GraphDatabase { return setProperties(delegate.createNode(), props); } - private T setProperties(T element, Property[] props) { - if (props==null || props.length==0) return element; - for (Property prop : props) { - element.setProperty(prop.name, prop.value); + private T setProperties(T primitive, Property... properties) { + assert primitive != null; + if (properties==null || properties.length==0) return primitive; + for (Property prop : properties) { + if (prop.value==null) { + primitive.removeProperty(prop.name); + } else { + primitive.setProperty(prop.name, prop.value); + } } - return element; + return primitive; } @Override diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java index 6636c9b7a..38b26ac58 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jOperations.java @@ -2,6 +2,7 @@ package org.springframework.data.graph.neo4j.template; import org.neo4j.graphdb.*; import org.neo4j.graphdb.traversal.TraversalDescription; +import org.springframework.data.graph.core.Property; import java.util.Map; @@ -40,11 +41,12 @@ public interface Neo4jOperations { * Two shortcut means of providing the properties (very short with static imports) * template.createNode(PropertyMap._("name","value")); * template.createNode(PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * + * * @param props properties to be set at node creation might be null - * @param indexFields fields that are automatically indexed from the given properties for the newly created ndoe * @return the newly created node */ - Node createNode(Map props, String... indexFields); + Node createNode(Property... props); /** * Delegates to the GraphDatabaseService @@ -59,14 +61,15 @@ public interface Neo4jOperations { * Two shortcut means of providing the properties (very short with static imports) * template.createRelationship(from,to,TYPE, PropertyMap._("name","value")); * template.createRelationship(from,to,TYPE, PropertyMap.props().set("name","value").set("prop","anotherValue").toMap(), "name", "prop"); + * + * * @param startNode start-node of relationship * @param endNode end-node of relationship * @param type relationship type, might by an enum implementing RelationshipType or a DynamicRelationshipType.withName("name") * @param props optional initial properties - * @param indexFields optional indexed fields * @return the newly created relationship */ - Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Map props, String... indexFields); + Relationship createRelationship(Node startNode, Node endNode, RelationshipType type, Property... props); /** * Queries the supplied index with a lucene query string or query object (if the neo4j-index provider is lucene) @@ -144,14 +147,4 @@ public interface Neo4jOperations { * @return the provided element for convenience */ T index(String indexName, T element, String field, Object value); - /** - * Auto-indexes all indexFields for the given element's properties if they exist - * @param indexName Name of the index, will be checked against existing indexes according to the given element - * assumes a "node" node index or "relationship" relationship index for a null value - * @param element node or relationship to auto-index - * @param indexProperties property names to index - * @param the provided element type - * @return the provided element for convenience - */ - T autoIndex(String indexName, T element, String... indexProperties); } diff --git a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java index f6e90caa7..0281d5e98 100644 --- a/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java +++ b/spring-data-neo4j/src/main/java/org/springframework/data/graph/neo4j/template/Neo4jTemplate.java @@ -25,8 +25,7 @@ import org.neo4j.helpers.collection.IterableWrapper; import org.springframework.dao.DataAccessException; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.graph.UncategorizedGraphStoreException; - -import java.util.Map; +import org.springframework.data.graph.core.Property; public class Neo4jTemplate implements Neo4jOperations { @@ -117,13 +116,13 @@ public class Neo4jTemplate implements Neo4jOperations { } @Override - public Node createNode(final Map properties, final String... indexFields) { + public Node createNode(final Property... properties) { return exec(new GraphCallback() { @Override public Node doWithGraph(GraphDatabaseService graph) throws Exception { Node node = graphDatabaseService.createNode(); if (properties == null) return node; - return autoIndex(null, setProperties(node, properties), indexFields); + return setProperties(node, properties); } }); } @@ -148,15 +147,6 @@ public class Neo4jTemplate implements Neo4jOperations { } } - @Override - public T autoIndex(String indexName, T element, String... indexFields) { - for (String indexField : indexFields) { - if (!element.hasProperty(indexField)) continue; - index(indexName,element, indexField,element.getProperty(indexField)); - } - return element; - } - @Override public T index(final String indexName, final T element, final String field, final Object value) { notNull(element, "element", field, "field", value, "value"); @@ -290,26 +280,26 @@ public class Neo4jTemplate implements Neo4jOperations { } @Override - public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Map properties, final String... indexFields) { + public Relationship createRelationship(final Node startNode, final Node endNode, final RelationshipType relationshipType, final Property... properties) { notNull(startNode, "startNode", endNode, "endNode", relationshipType, "relationshipType", properties, "properties"); return exec(new GraphCallback() { @Override public Relationship doWithGraph(GraphDatabaseService graph) throws Exception { Relationship relationship = startNode.createRelationshipTo(endNode, relationshipType); if (properties == null) return relationship; - return autoIndex("relationship", setProperties(relationship, properties), indexFields); + return setProperties(relationship, properties); } }); } - private T setProperties(T primitive, Map properties) { + private T setProperties(T primitive, Property... properties) { assert primitive != null; if (properties==null) return primitive; - for (Map.Entry prop : properties.entrySet()) { - if (prop.getValue()==null) { - primitive.removeProperty(prop.getKey()); + for (Property prop : properties) { + if (prop.value==null) { + primitive.removeProperty(prop.name); } else { - primitive.setProperty(prop.getKey(), prop.getValue()); + primitive.setProperty(prop.name, prop.value); } } return primitive; diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java index 98278f4a8..7c7a4065b 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/core/GraphDatabaseFactoryTest.java @@ -2,7 +2,7 @@ package org.springframework.data.graph.core; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.data.graph.neo4j.support.LocalGraphDatabase; +import org.springframework.data.graph.neo4j.support.DelegatingGraphDatabase; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsInstanceOf.instanceOf; @@ -22,7 +22,7 @@ public class GraphDatabaseFactoryTest { try { GraphDatabase graphDatabase = ctx.getBean("graphDatabase", GraphDatabase.class); assertThat(graphDatabase, is(not(nullValue()))); - assertThat(graphDatabase, is(instanceOf(LocalGraphDatabase.class))); + assertThat(graphDatabase, is(instanceOf(DelegatingGraphDatabase.class))); } finally { ctx.close(); } @@ -35,7 +35,7 @@ public class GraphDatabaseFactoryTest { factory.setStoreLocation("target/test-db"); GraphDatabase graphDatabase = factory.getObject(); assertThat(graphDatabase, is(not(nullValue()))); - assertThat(graphDatabase,is(instanceOf(LocalGraphDatabase.class))); + assertThat(graphDatabase,is(instanceOf(DelegatingGraphDatabase.class))); } finally { factory.shutdown(); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java index 0374db728..de6c7f1b0 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/Neo4jTemplateApiTest.java @@ -19,8 +19,7 @@ import java.util.Iterator; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.junit.Assert.*; -import static org.springframework.data.graph.neo4j.template.PropertyMap._; -import static org.springframework.data.graph.neo4j.template.PropertyMap.props; +import static org.springframework.data.graph.core.Property._; /** * @author mh @@ -189,7 +188,7 @@ public class Neo4jTemplateApiTest { @Test public void testCreateNodeWithProperties() throws Exception { - Node node=template.createNode(props().set("test", "testCreateNodeWithProperties").toMap()); + Node node=template.createNode(_("test", "testCreateNodeWithProperties")); assertTestPropertySet(node, "testCreateNodeWithProperties"); } diff --git a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java index 80d4cfb9a..3fc670fb4 100644 --- a/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java +++ b/spring-data-neo4j/src/test/java/org/springframework/data/graph/neo4j/template/NeoTraversalTest.java @@ -13,8 +13,8 @@ import java.util.Set; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.neo4j.kernel.Traversal.returnAllButStartNode; +import static org.springframework.data.graph.core.Property._; import static org.springframework.data.graph.neo4j.template.NeoTraversalTest.Type.HAS; -import static org.springframework.data.graph.neo4j.template.PropertyMap._; public class NeoTraversalTest extends NeoApiTest {