TransactionEventHandler gives javax.transaction.SystemException on Node.setProperty() - java

I'm trying to implement a TransactionEventHandler like the one used in neo4j-versioning in order to create a time-machine style, versioned Neo4j database, now using Neo4j 2.x. It fails with the following infinite stack trace:
javax.transaction.SystemException: TM has encountered some problem, please perform necessary action (tx recovery/restart)
at org.neo4j.kernel.impl.transaction.TxManager.assertTmOk(TxManager.java:349)
at org.neo4j.kernel.impl.transaction.TxManager.setRollbackOnly(TxManager.java:758)
at org.neo4j.kernel.TransactionEventHandlers.beforeCompletion(TransactionEventHandlers.java:120)
at org.neo4j.kernel.impl.core.TransactionEventsSyncHook.beforeCompletion(TransactionEventsSyncHook.java:68)
at org.neo4j.kernel.impl.transaction.TransactionImpl.doBeforeCompletion(TransactionImpl.java:368)
at org.neo4j.kernel.impl.transaction.TxManager.commit(TxManager.java:398)
at org.neo4j.kernel.impl.core.IsolatedTransactionTokenCreator.getOrCreate(IsolatedTransactionTokenCreator.java:61)
at org.neo4j.kernel.impl.core.TokenHolder.createToken(TokenHolder.java:114)
at org.neo4j.kernel.impl.core.TokenHolder.getOrCreateId(TokenHolder.java:102)
at org.neo4j.kernel.impl.api.store.DiskLayer.propertyKeyGetOrCreateForName(DiskLayer.java:367)
at org.neo4j.kernel.impl.api.store.CacheLayer.propertyKeyGetOrCreateForName(CacheLayer.java:370)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.propertyKeyGetOrCreateForName(StateHandlingStatementOperations.java:939)
at org.neo4j.kernel.impl.api.DataIntegrityValidatingStatementOperations.propertyKeyGetOrCreateForName(DataIntegrityValidatingStatementOperations.java:67)
at org.neo4j.kernel.impl.api.OperationsFacade.propertyKeyGetOrCreateForName(OperationsFacade.java:397)
at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:205)
...
This is my test:
#Test
public void test() {
GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node referenceNode = null;
try (Transaction transaction = graphDb.beginTx()) {
referenceNode = graphDb.createNode();
transaction.success();
}
VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode);
graphDb.registerTransactionEventHandler(versioningTransactionEventHandler);
try (Transaction tx = graphDb.beginTx()) {
Node node = graphDb.createNode();
tx.success();
}
graphDb.shutdown();
}
This is the VersioningTransactionEventHandler:
public class VersioningTransactionEventHandler implements TransactionEventHandler<Object> {
private final Node versionDataNode;
public VersioningTransactionEventHandler(Node versionDataNode) {
this.versionDataNode = versionDataNode;
}
#Override
public Object beforeCommit(TransactionData data) throws Exception {
versionDataNode.setProperty("foo", "bar"); // <- this causes the error
return null;
}
#Override
public void afterCommit(TransactionData data, Object state)
{
}
#Override
public void afterRollback(TransactionData data, Object state)
{
}
}
I'm using org.neo4j.neo4j-2.0.1 and org.neo4j.neo4j-kernel-2.0.1 in my application.
Why is the setProperty() causing this error? How can I fix it? Any clues to what may be wrong here is greatly appreciated.
Update
As Michael Hunger suggested I did a setProperty() before passing the node in, but now the test silently hangs for infinity and nothing happens. It doesn't matter what property key-value pair is set on the node:
...
referenceNode = graphDb.createNode();
referenceNode.setProperty("foo", "bar"); // <- results in hang
referenceNode.setProperty("herp", "derp"); // <- results in hang also
...
Still any clues? I just want to manipulate a node while inside the transaction event handler like it was done in the 1.9 version, but Neo4j 2.x doesn't have the GraphDatabaseService#getReferenceNode() method which is passed in the constructor there.

Firstly, it seems like you'd want to call incrementTransactionVersion() in your TransactionEventHandler's afterCommit() method. This way, you know that the transaction was successfully committed before you increment the version. I assume that the circularity is caused by the fact the incrementing the version property of the version Node causes another transaction. To work around this, you could check the TransactionData to see if the current transaction was caused by the change to the version Node. This isn't as straightforward as it might be, but here's some pseudo-Java (I haven't even tested that it compiles) that illustrates how this might be done:
Iterable<PropertyEntry<Node>> ii = txData.assignedNodeProperties();
boolean doIncrementVersion = false;
for (PropertyEntry<Node> pEntry : txData.assignedNodeProperties()) {
if (pEntry.key().equals("transactionVersion") {
doIncrementVersion = true;
break;
}
}
if (doIncrementVersion) {
incrementTransactionVersion();
}
Hope this helps!

I managed to get rid of the circular error mess by adding a line to the intialization code that creates the version node in the test:
#BeforeClass
public static void setup() {
db = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node versionNode = null;
try (Transaction tx = db.beginTx()) {
versionNode = db.createNode(DynamicLabel.label("__TransactionVersion__"));
versionNode.setProperty("transactionVersion", 0L); // <---
tx.success();
}
transactionEventHandler = new MyTransactionEventHandler(versionNode);
db.registerTransactionEventHandler(transactionEventHandler);
}
Setting the property after creating the node seems to fix it.
So the error seems to be caused by referring to an unknown "token" transactionVersion inside the transaction event handler in the incrementVersionNumber() method, which causes the error.
Apparently, this is a
bug in Neo4j.

This seems to be a bug, caused by the on-the-fly creation of the internal token for the "name" of the property.
Can you try to use that property name before so that the token gets created before?
Just tried it, if you do
#Test
public void test() {
GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node referenceNode = null;
try (Transaction transaction = graphDb.beginTx()) {
referenceNode = graphDb.createNode();
// use the "foo" property-name once before
referenceNode.setProperty("foo", "bar");
transaction.success();
}
VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode);
graphDb.registerTransactionEventHandler(versioningTransactionEventHandler);
try (Transaction tx = graphDb.beginTx()) {
Node node = graphDb.createNode();
tx.success();
}
graphDb.shutdown();
}

I found a workaround the last hang happening when I called setProperty() in beforeCommit(). It seems it only happens on non-write transactions (I thought beforeCommit() was only called on write operations)?
So then, if I check that before doing the property setting, the hanging disappears:
...
#Override
public Object beforeCommit(TransactionData data) throws Exception {
if (containsWriteChanges(data)) {
versionDataNode.setProperty("foo", "bar");
}
return null;
}
private boolean containsWriteChanges(TransactionData data) {
return data.assignedNodeProperties().iterator().hasNext()
|| data.assignedRelationshipProperties().iterator().hasNext()
|| data.createdNodes().iterator().hasNext()
|| data.createdRelationships().iterator().hasNext()
|| data.deletedNodes().iterator().hasNext()
|| data.deletedRelationships().iterator().hasNext()
|| data.removedNodeProperties().iterator().hasNext()
|| data.removedRelationshipProperties().iterator().hasNext();
}
...

Related

Getting following error "org.neo4j.kernel.api.exceptions.TransactionFailureException: Transaction rolled back even if marked as successful" at neo4j

Wrote stored proc in neo4j and then added it in the neo4j plugins. When calling the stored proc getting the error"
org.neo4j.kernel.api.exceptions.TransactionFailureException:
Transaction rolled back even if marked as successful
Below are some information regarding how I am doing.
#Procedure("example.search12")
public Stream<SearchHit> searchData(
#Name("phoneNumber") String phoneNumber,
#Name("searchText") String searchText) throws InterruptedException, ExecutionException {
List<SearchHit> resultList = new ArrayList<>();
try {
Node startNode = getStartNode(phoneNumber);
if(null == startNode){
System.out.println("Phone Number not found::"+ phoneNumber);
return null;
}
final Set<Node> results = new LinkedHashSet<>();
innerSeacrh(db, startNode, searchText, 1,results);
List<Node> nodes = new ArrayList<Node>();
for (Node node : results) {
System.out.println(node.getProperty("fullname"));
nodes.add(node);
resultList.add(new SearchHit(node));
}
} catch(Exception ex) {
ex.printStackTrace();
}
return resultList.stream();
}
public static class SearchHit {
// This records contain a single field named 'nodeId'
public long nodeId;
public SearchHit(Node node) {
this.nodeId = node.getId();
}
}
I am calling the stored proc by below command:
call example.search12("919818131043","anu");
Getting below error.
org.neo4j.kernel.api.exceptions.TransactionFailureException:
Transaction rolled back even if marked as successful
Please help i need to resolve it as soon as possible.
I have solved this issue. Actually when we hit the stored proc it implicitly opens a transaction so if you try to open another transaction in the same thread. it gives problems.

How to set property of node inside TransactionEventHandler? [duplicate]

I'm trying to implement a TransactionEventHandler like the one used in neo4j-versioning in order to create a time-machine style, versioned Neo4j database, now using Neo4j 2.x. It fails with the following infinite stack trace:
javax.transaction.SystemException: TM has encountered some problem, please perform necessary action (tx recovery/restart)
at org.neo4j.kernel.impl.transaction.TxManager.assertTmOk(TxManager.java:349)
at org.neo4j.kernel.impl.transaction.TxManager.setRollbackOnly(TxManager.java:758)
at org.neo4j.kernel.TransactionEventHandlers.beforeCompletion(TransactionEventHandlers.java:120)
at org.neo4j.kernel.impl.core.TransactionEventsSyncHook.beforeCompletion(TransactionEventsSyncHook.java:68)
at org.neo4j.kernel.impl.transaction.TransactionImpl.doBeforeCompletion(TransactionImpl.java:368)
at org.neo4j.kernel.impl.transaction.TxManager.commit(TxManager.java:398)
at org.neo4j.kernel.impl.core.IsolatedTransactionTokenCreator.getOrCreate(IsolatedTransactionTokenCreator.java:61)
at org.neo4j.kernel.impl.core.TokenHolder.createToken(TokenHolder.java:114)
at org.neo4j.kernel.impl.core.TokenHolder.getOrCreateId(TokenHolder.java:102)
at org.neo4j.kernel.impl.api.store.DiskLayer.propertyKeyGetOrCreateForName(DiskLayer.java:367)
at org.neo4j.kernel.impl.api.store.CacheLayer.propertyKeyGetOrCreateForName(CacheLayer.java:370)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.propertyKeyGetOrCreateForName(StateHandlingStatementOperations.java:939)
at org.neo4j.kernel.impl.api.DataIntegrityValidatingStatementOperations.propertyKeyGetOrCreateForName(DataIntegrityValidatingStatementOperations.java:67)
at org.neo4j.kernel.impl.api.OperationsFacade.propertyKeyGetOrCreateForName(OperationsFacade.java:397)
at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:205)
...
This is my test:
#Test
public void test() {
GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node referenceNode = null;
try (Transaction transaction = graphDb.beginTx()) {
referenceNode = graphDb.createNode();
transaction.success();
}
VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode);
graphDb.registerTransactionEventHandler(versioningTransactionEventHandler);
try (Transaction tx = graphDb.beginTx()) {
Node node = graphDb.createNode();
tx.success();
}
graphDb.shutdown();
}
This is the VersioningTransactionEventHandler:
public class VersioningTransactionEventHandler implements TransactionEventHandler<Object> {
private final Node versionDataNode;
public VersioningTransactionEventHandler(Node versionDataNode) {
this.versionDataNode = versionDataNode;
}
#Override
public Object beforeCommit(TransactionData data) throws Exception {
versionDataNode.setProperty("foo", "bar"); // <- this causes the error
return null;
}
#Override
public void afterCommit(TransactionData data, Object state)
{
}
#Override
public void afterRollback(TransactionData data, Object state)
{
}
}
I'm using org.neo4j.neo4j-2.0.1 and org.neo4j.neo4j-kernel-2.0.1 in my application.
Why is the setProperty() causing this error? How can I fix it? Any clues to what may be wrong here is greatly appreciated.
Update
As Michael Hunger suggested I did a setProperty() before passing the node in, but now the test silently hangs for infinity and nothing happens. It doesn't matter what property key-value pair is set on the node:
...
referenceNode = graphDb.createNode();
referenceNode.setProperty("foo", "bar"); // <- results in hang
referenceNode.setProperty("herp", "derp"); // <- results in hang also
...
Still any clues? I just want to manipulate a node while inside the transaction event handler like it was done in the 1.9 version, but Neo4j 2.x doesn't have the GraphDatabaseService#getReferenceNode() method which is passed in the constructor there.
Firstly, it seems like you'd want to call incrementTransactionVersion() in your TransactionEventHandler's afterCommit() method. This way, you know that the transaction was successfully committed before you increment the version. I assume that the circularity is caused by the fact the incrementing the version property of the version Node causes another transaction. To work around this, you could check the TransactionData to see if the current transaction was caused by the change to the version Node. This isn't as straightforward as it might be, but here's some pseudo-Java (I haven't even tested that it compiles) that illustrates how this might be done:
Iterable<PropertyEntry<Node>> ii = txData.assignedNodeProperties();
boolean doIncrementVersion = false;
for (PropertyEntry<Node> pEntry : txData.assignedNodeProperties()) {
if (pEntry.key().equals("transactionVersion") {
doIncrementVersion = true;
break;
}
}
if (doIncrementVersion) {
incrementTransactionVersion();
}
Hope this helps!
I managed to get rid of the circular error mess by adding a line to the intialization code that creates the version node in the test:
#BeforeClass
public static void setup() {
db = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node versionNode = null;
try (Transaction tx = db.beginTx()) {
versionNode = db.createNode(DynamicLabel.label("__TransactionVersion__"));
versionNode.setProperty("transactionVersion", 0L); // <---
tx.success();
}
transactionEventHandler = new MyTransactionEventHandler(versionNode);
db.registerTransactionEventHandler(transactionEventHandler);
}
Setting the property after creating the node seems to fix it.
So the error seems to be caused by referring to an unknown "token" transactionVersion inside the transaction event handler in the incrementVersionNumber() method, which causes the error.
Apparently, this is a
bug in Neo4j.
This seems to be a bug, caused by the on-the-fly creation of the internal token for the "name" of the property.
Can you try to use that property name before so that the token gets created before?
Just tried it, if you do
#Test
public void test() {
GraphDatabaseService graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
Node referenceNode = null;
try (Transaction transaction = graphDb.beginTx()) {
referenceNode = graphDb.createNode();
// use the "foo" property-name once before
referenceNode.setProperty("foo", "bar");
transaction.success();
}
VersioningTransactionEventHandler versioningTransactionEventHandler = new VersioningTransactionEventHandler(referenceNode);
graphDb.registerTransactionEventHandler(versioningTransactionEventHandler);
try (Transaction tx = graphDb.beginTx()) {
Node node = graphDb.createNode();
tx.success();
}
graphDb.shutdown();
}
I found a workaround the last hang happening when I called setProperty() in beforeCommit(). It seems it only happens on non-write transactions (I thought beforeCommit() was only called on write operations)?
So then, if I check that before doing the property setting, the hanging disappears:
...
#Override
public Object beforeCommit(TransactionData data) throws Exception {
if (containsWriteChanges(data)) {
versionDataNode.setProperty("foo", "bar");
}
return null;
}
private boolean containsWriteChanges(TransactionData data) {
return data.assignedNodeProperties().iterator().hasNext()
|| data.assignedRelationshipProperties().iterator().hasNext()
|| data.createdNodes().iterator().hasNext()
|| data.createdRelationships().iterator().hasNext()
|| data.deletedNodes().iterator().hasNext()
|| data.deletedRelationships().iterator().hasNext()
|| data.removedNodeProperties().iterator().hasNext()
|| data.removedRelationshipProperties().iterator().hasNext();
}
...

Unusable session in Modeshape - JCR Exception

Sometimes when I use multiple Modeshape actions inside one function I get this error:
javax.jcr.RepositoryException: The session with an ID of '060742fc6' has been closed and can no longer be used.
I couldn't find any explanations of this on the web. Here is what I call:
myFunction( service.doSomething ( service.getStuff ( id, "en_EN" ).getPath() ) );
doSomething, getStuff:
#Interceptors({Foo.class, TraceInterceptor.class})
#Override
public Node doSomething(final String bar) throws RepositoryException {
return modeshape.execute(new JcrHandler<Node>() {
#Override
public Node execute(Session session) throws RepositoryException {
return session.getNode(bar);
}
});
}
#Interceptors(TraceInterceptor.class)
#Override
public ObjectExtended getStuff(final String query, final String language)
throws RepositoryException {
return modeshape.execute(new JcrHandler<ObjectExtended>() {
#Override
public ObjectExtendedexecute(Session session)
throws RepositoryException {
QueryManager queryManager = session.getWorkspace().getQueryManager();
ObjectExtendeditem = null;
String queryWrapped =
"select * from [th:this] as c where name(c)='lang_"
+ language + "' and c.[th:mylabel] "
+ "= '" + queryStr + "'";
LOGGER.debug("Query: " + queryWrapped);
Query query =
queryManager.createQuery(queryWrapped,Query.JCR_SQL2);
QueryResult result = query.execute();
NodeIterator iter = result.getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode().getParent();
if (node.isNodeType("th:term")) {
item = new ObjectExtended();
item.setLabel(getLabel(language, node));
item.setPath(node.getPath());
}
}
return item;
}
});
}
Why is this happening please? What am I doing wrong?
That error message means one of two thing: either the repository is being shutdown, or the Session.logout() method is being called.
None of the above code shows how your sessions are being managed, and you don't say whether you are using a framework. But I suspect that somehow you are holding onto a Session too long (perhaps after your framework is closing the session), or the Session is leaking to multiple threads, and one thread is attempting to use it after the other has closed it.
The latter could be a real problem: while passing a single Session instance from one thread to another is okay (as long as the original thread no longer uses it), but per the JCR 2.0 specification Session instances are not threadsafe and should not be concurrently used by multiple threads.
If you're creating the Session in your code, it's often good to use a try-finally block:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) {
try {
session.logout();
} finally {
session = null;
}
}
}
Note that logout() does not throw a RepositoryException, so the above form usually works well. Of course, if you know you're not using session later on in the method, you don't need the inner try-finally to null the session reference:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) session.logout();
}
This kind of logic can easily be encapsulated.

Using SpringRestGraphDatabase API to work within transaction

I'm new to graph db and i'm having problems to get the api work within a transaction.
I have a simple code that uses the neo4j graph db api to create nodes and relationship. My code runs in JUnit and tries to create 2 nodes and a relationship between them using begin and end transaction given below.
The code works fine in a happy scenario. However, if something fails within the code, the nodes are still committed into the graph database. Not sure if i'm doing something wrong out here. I would have expected the 2 nodes created to be rolled back.
Here is the code snippet:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/applicationContext.xml" })
public class RestBatchLoaderTest {
#Autowired
SpringRestGraphDatabase graphDatabaseService;
#Test
public void createNode() {
Transaction tx =graphDatabaseService.beginTx();
try {
Map<String,Object> nodeprops1 = new HashMap<String, Object>();
nodeprops1.put("name", "James Parker");
nodeprops1.put("age", Integer.valueOf(11));
Node james = graphDatabaseService.createNode(nodeprops1);
Assert.assertNotNull(james);
Map<String,Object> nodeprops2 = new HashMap<String, Object>();
nodeprops2.put("name", "Bing P");
nodeprops2.put("age", Integer.valueOf(34));
Node bing= graphDatabaseService.createNode(nodeprops2);
Node aa = null;
// Failure point: should rollback the previous node in the finally.
graphDatabaseService.remove(aa);
Map<String,Object> relprops = new HashMap<String, Object>();
RelationshipType type = new RelationshipType() {
#Override
public String name() {
return "MARRIED_TO";
}
};
graphDatabaseService.createRelationship(joe, jane, type, relprops);
tx.success();
} finally {
tx.finish();
}
}
The graphDatabaseService object is autowired using spring configuration. Here is the configuration:
<neo4j:config graphDatabaseService="graphDatabaseService"/>
<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
<constructor-arg value="http://localhost:7474/db/data/"/>
</bean>
Also, I notice tx object is an instance of NullTransaction when graphDatabaseService.beginTx() is called in the code above.
Any ideas, what is going wrong?
Thanks.
I think figured out what the problem was. The configuration needs to have batch enabled - true. Also i used the RestAPI wrapper to the graph database object to run it as one atomic code. See code below:
#Autowired
SpringRestGraphDatabase graphDatabaseService;
private RestAPI restAPI;
#Before
public void init(){
this.restAPI = ((RestGraphDatabase)graphDatabaseService).getRestAPI();
}
#Test
public void testEnableBatchTransactions() throws Exception {
System.setProperty(Config.CONFIG_BATCH_TRANSACTION,"true");
Transaction tx = restAPI.beginTx();
try {
Node n1 = restAPI.createNode(map("name", "node1"));
Node n2 = restAPI.createNode(map("name", "node2"));
Node n3 = restAPI.createNode(map("name", "node3"));
//String s = null;
//s.toString();
Node n4 = restAPI.createNode(map("name", "node4"));
tx.success();
} finally {
tx.finish();
}
assertTrue(tx instanceof BatchTransaction);
}
Also System.setProperty(Config.CONFIG_BATCH_TRANSACTION,"true"); enables the batch mode.
To test this, try un-commenting the code snippet and run the test. Nodes n1, n2 and n3 will not be committed in the db.
You specified graphDatabaseService.remove(aa); as your failure point as aa is NULL. Looking into the documentation of org.springframework.data.neo4j.rest.SpringRestGraphDatabase there is no Exception documented that is being thrown if the node is NULL. Have you verified that an exception is actually thrown? Otherwise your code will run through to tx.success();. If an exception is thrown, please specify further what version of neo4j and spring you are using.
Edit:
After reading a little more, I see in the source of org.springframework.data.neo4j.rest.SpringRestGraphDatabase that it should give you a NullTransaction that basically does nothing (see here).
Furthermore, the Spring Data neo4j documentationstates that each operation is in its own transaction as the neo4j REST adapter does not allow transaction that span over multiple operations (see here).

NullPointerException accessing a Neo4J database in a Singleton

I'm working on a Neo4J database, using it in a little social network. The idea is:
As a user, you connect to the website, and it connects you to a N4J db.
I tried to avoid problem related to multiple instances of the db by creating it through a Singleton. However, I think my users must be able to "connect" and "disconnect", so I created method for that, but when I use disconnect() on a user, the others run through a NullPointerException due to the fact that they can't access the db anymore (which I thought was handled by the Singleton).
Here is the code, which I believe will make things clearer :
The main that I use to test my code :
public static void main(String[] args) {
Node root;
N4JAdmin admin = new N4JAdmin();
Transaction tx = GraphDB.getGraphDb().beginTx();
try {
root = GraphDB.getGraphDb().createNode();
root.setProperty("Nom", "aname");
root.setProperty("Prenom", "afirstname");
tx.success();
} finally {
tx.finish();
}
N4JUser sr = admin.addUser("Ribeiro", "Swen", 1);
//14 more addUser, it creates nodes in the db with the 3 properties, works fine
// The following connect() calls are commented because the errors occurs with or without them
// sr.connect();
// the 14 others...
sr.addAcquaintance(cw.getUserNode());
sr.addAcquaintance(fc.getUserNode());
sr.disconnect();
System.out.println("+++++++++ sr disconnected ++++++++++");
bm.addAcquaintance(cm.getUserNode());
// and more operations...
GraphDB.getGraphDb().shutdown();
}
the addAquaintance code :
public void addAcquaintance(Node target) {
Transaction tx = GraphDB.getGraphDb().beginTx();
try {
this.userNode.createRelationshipTo(target, RelTypes.CONNAIT);
System.out.println(this.userNode.getProperty("Nom")+" "+this.userNode.getProperty("Prenom")+" est maintenant ami avec : "+ target.getProperty("Nom")+" "+target.getProperty("Prenom"));
tx.success();
} finally {
tx.finish();
}
}
And the GraphDB class, which is my Singleton :
public final class GraphDB {
private static final String DB_PATH = "/Workties/database/workties.db";
private static GraphDatabaseService graphDb;
private static boolean instanciated = false;
private GraphDB(){
GraphDB.graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH);
registerShutdownHook(graphDb);
GraphDB.instanciated = true;
}
private static void registerShutdownHook( final GraphDatabaseService graphDb ){
Runtime.getRuntime().addShutdownHook( new Thread(){
#Override
public void run(){
graphDb.shutdown();
}
} );
}
public static GraphDatabaseService getGraphDb(){
if (!GraphDB.instanciated) {
new GraphDB();
System.out.println("New connection : " +GraphDB.graphDb.toString());
}
return GraphDB.graphDb;
}
}
Note : the instanciated attribute was added later, but even without it it didn't work.
My problem comes from the fact that I thought this type of error wouldn't happen using a Singleton, so I'm a bit stuck...
Thanks in advance for your help !
EDIT : the disconnect method :
public void disconnect(){
GraphDB.getGraphDb().shutdown();
}
You should not have to call shutdown for every user- once the DB is instantiated for the first time, it is enough to last you for all your querying etc. Why do you want to connect and disconnect?
The nullpointer is because shutdown shuts down Neo4j but your singleton still thinks instanciated=true, so it returns a handle to a graphDb that isn't really there. Even without the instanciated variable which you added, the singleton maintains a reference to graphDb after it's shutdown.
Strongly advise against doing the shutdown at all unless your app is terminating.

Categories

Resources