NullPointerException accessing a Neo4J database in a Singleton - java

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.

Related

Hibernate and multiThread Logic

Im working on a java standAlone project. I need to use hibernate in a MultiThread application but i just cant figure it out how to set up this correctly.
Each Thread deals with the same process of the others.
Everything goes Ok when i run it in a Non-Async way, but when i call the same thing using threads, hibernate just don't work fine.
Can anyone please explain me what's the correct way to use Hibernate in a multiThread Java Stand-Alone App?
Hibernate Util
public class HibernateUtil {
private static final Session session;
static {
try {
SessionFactory sessionFactory;
Properties properties = new Properties();
properties.load(new FileInputStream("middleware.properties"));
Configuration cfg = new Configuration().configure();
cfg.addProperties(properties);
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
} catch (IOException | HibernateException he) {
JOptionPane.showMessageDialog(null, DataBaseMessage.CONNECTION_ERROR.getMessage(), DataBaseMessage.CONNECTION_ERROR.getTitle(),JOptionPane.ERROR_MESSAGE);
throw new ExceptionInInitializerError(he);
}
}
public static Session getSession() {
return session;
}
The Error comes here
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
public synchronized List<TbHistoDespachos> ExractDespachoAndNotify(String data, String nombreConexion) {
List<TbHistoDespachos> despachos = new ArrayList<>();
String nombreConexionUpp = nombreConexion.toUpperCase();
try {
Document doc = convertStringToDocument(data);
if (!doc.getRootElement().getChild("reply").getChild("readTagIDs")
.getChildren().isEmpty()) {
for (Element element : doc.getRootElement().getChild("reply").
getChild("readTagIDs").getChild("returnValue")
.getChildren()) {
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
if (despacho != null) {
if(evaluateDespacho(nombreConexionUpp, despacho)){
despachos.add(despacho);
}
}
}
}
} catch (JDOMException | IOException ex) {
JOptionPane.showMessageDialog(null, FilesMessageWarnings.NOTIFICATION_SAP_WARNING.
getMessage().replace("&nombreConexion", nombreConexion).replace("&tagID", ""),
FilesMessageWarnings.NOTIFICATION_SAP_WARNING.getTitle(), JOptionPane.WARNING_MESSAGE);
}
return despachos;
}
Here is the DAO
public class Dao {
private static Session sesion;
public static TbHistoDespachos findDespachoByTagId(String tagId) {
TbHistoDespachos despacho = null;
try {
startTransmission();
despacho = (TbHistoDespachos)sesion.createQuery("FROM TbHistoDespachos WHERE TAG_ID =:tagId")
.setParameter("tagId", tagId)
.uniqueResult();
stopTransmission();
} catch (HibernateException he) {
System.out.println("error: " + he.getMessage());
JOptionPane.showMessageDialog(null, DataBaseMessage.QUERY_ERROR.getMessage(),
DataBaseMessage.QUERY_ERROR.getTitle(), JOptionPane.ERROR_MESSAGE);
}
return despacho;
}
private static void startTransmission() {
sesion = HibernateUtil.getSession();
sesion.getTransaction().begin();
}
private static void stopTransmission() {
sesion.getTransaction().commit();
sesion.getSessionFactory().getCurrentSession().close();
sesion.clear();
}
ANY IDEAS?
The problem stems from static Session variables. A SessionFactory is thread-safe and, generally speaking, you only need one (static) instance per database. A Session, on the other hand, is not thread-safe and is usually created (using a SessionFactory) and discarted/closed on the fly.
To solve your immediate problem, remove the static Session sesion variable from your Dao and also 'inline' the startTransmission and stopTransmission methods in the findDespachoByTagId method. This will ensure that each thread calling findDespachoByTagId creates and uses its own session instance. To analyze the current problem, imagine two threads calling findDespachoByTagId at the same time. Now the static session variable will be assigned a value twice by the startTransmission method. This means one session instance is lost almost immediatly after it was created while the other one is used by two threads at the same time. Not a good thing.
But there are other problems too: there are no finally blocks that guarantee transactions are closed and database connections are released (via the closing of sessions). Also, you will probably want to use a database pool as the one provided by Hibernate is not suitable for production. I recommend you have a look at HibHik: I created this project to show a minimal stand-alone Java application using Hibernate with a database pool (HikariCP) that uses the recommended patterns and practices (mostly shown in TestDbCrud.java). Use the relevant parts in your application, than write multi-threaded unit-tests to verify your database layer (DAO) is working properly, even in the case of failure (e.g. when the database is suddenly no longer available because the network-cable was unplugged).

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();
}
...

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

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();
}
...

ServletContext attributes and static properties

So, I'm trying to make a Mongo connection that remains persistent across several uses of the application (page accesses). I'm tired of all these connections building up during my testing, and I want to use proper procedure by having that single persistent connection. Best practices and all that.
Here's what I have:
web.xml
<listener>
<listener-class>com.core.tools.MyServletContextListener</listener-class>
</listener>
com.core.tools.MyServletContextListener
public class MyServletContextListener implements ServletContextListener {
private static Logger log = org.apache.log4j.Logger.getLogger(MyServletContextListener.class);
public void contextInitialized( ServletContextEvent sce ){
try {
EntityManager.setupMongoClient(sce);
} catch (UnknownHostException e) {
log.error("Error setting up Servlet Context");
}
}
public void contextDestroyed( ServletContextEvent sce ){
EntityManager.closeMongoClient(sce);
}
}
EntityManager
public class EntityManager {
private static MongoClient mongoConnection = null;
private static Jongo jongoDatasource = null;
public static void setupMongoClient( ServletContextEvent sce ) throws UnknownHostException{
if( sce.getServletContext().getAttribute("mongo") == null ){
mongoConnection = new MongoClient("localhost");
sce.getServletContext().setAttribute("mongo", mongoConnection );
}else if (mongoConnection == null) {
mongoConnection = (MongoClient) sce.getServletContext().getAttribute("mongo");
}
}
public static Jongo getJongoDatasource(){
if( jongoDatasource == null ){
jongoDatasource = new Jongo(mongoConnection.getDB("coreTest"));
}
return jongoDatasource;
}
public static void closeMongoClient( ServletContextEvent sce ){
if( sce.getServletContext().getAttribute("mongo") != null ){
mongoConnection = (MongoClient) sce.getServletContext().getAttribute("mongo");
mongoConnection.close();
}
}
}
What happens:
Good news - now my connections get cleaned up when the server closes.
Bad news - I still keep getting lots and lots of connections being created. Every time I hit the button, it makes a new connection ... but only for about 4 or 5 connections. Then it suddenly stops adding new connections for a while. Then it'll add 3 more. Then wait. Then another 2. Then nothing. Then a few minutes later, suddenly it'll add another 5 connections.
Confusing: I don't even know how it's managing to make those connections. Tracking the method calls - setupMongoClient is only called once - when the apache server starts up. It's the only place in the whole system that sets mongoConnection.
Whenever I press the button, mongoConnection is not set. And yet, the data is pulled from the mongo database, and testing mongoConnection shows me that it is not null.
How is mongoConnection not null? And why are new connections sporadically being made?
MongoClient is a connection pool; it maintains a number of open connections to the database for performance (creating new connection is costly).
when you call mongoConnection.getDB("DB"), MongoClient will retrieve a database connection from the connection pool. if there are no available connections in the connection pool, it will create a new connection and add it to the pool.
when you call mongoConnection.close(), the connection is not tear down. it is simply returned to the connection pool to be reused.
by default, the pool size is 5. this explains why you will eventually have 5 open connections even though you have closed them (remember, "closed" connections are returned to the pool). you can change this size

neo4j rest graphdb hangs when connecting to remote heroku instance

public class Test
{
private static RestAPI rest = new RestAPIFacade("myIp","username","password");
public static void main(String[] args)
{
Map<String, Object> foo = new HashMap<String, Object>();
foo.put("Test key", "testing");
rest.createNode(foo);
}
}
No output it just hangs on connection indefinitely.
Environment:
Eclipse
JDK 7
neo4j-rest-binding 1.9: https://github.com/neo4j/java-rest-binding
Heroku
Any ideas as to why this just hangs?
The following code works:
public class Test
{
private static RestAPI rest = new RestAPIFacade("myIp","username","password");
public static void main(String[] args)
{
Node node = rest.getNodeById(1);
}
}
So it stands that I can correctly retrieve remote values.
I guess this is caused by lacking usage of transactions. By default neo4j-rest-binding aggregates multiple operations into one request (aka one transaction). There are 2 ways to deal with this:
change transactional behaviour to "1 operation = 1 transaction" by setting
-Dorg.neo4j.rest.batch_transaction=false for your JVM. Be aware this could impact performance since every atomic operation is a seperate REST request.
use transactions in your code:
.
RestGraphDatabse db = new RestGraphDatabase("http://localhost:7474/db/data",username,password);
Transaction tx = db.beginTx();
try {
Node node = db.createNode();
node.setPropery("key", "value");
tx.success();
} finally {
tx.finish();
}

Categories

Resources