Hello i am working with mongodb java driver. In their documentation, they mentioned that,
The MongoClient class is designed to be thread safe and shared among threads.
Typically you create only 1 instance for a given database cluster and use it across
your application.
So, I want to make this object available for every user. how can i do this?
The best way to do this is to use Singleton design pattern. This is the code-
public class MongoDBManager {
public MongoClient mongoClient = null;
String host = "127.0.0.1";
static MongoDBManager mongo=new MongoDBManager();
private MongoDBManager() {
try {
mongoClient = new MongoClient( host , 27017);
} catch (UnknownHostException e) {
System.err.println("Connection errors");
e.printStackTrace();
}
}
public static MongoDBManager getInstance(){
return mongo;
}
}
Call only MongoDBManager.getInstance() whenever you need connection. only one object will be used.
Related
I have written a RESTful API using Apache Jersey. I am using MongoDB as my backend. I used Morphia (v.1.3.4) to map and persist POJO to database. I tried to follow "1 application 1 connection" in my API as recommended everywhere but I am not sure I am successful. I run my API in Tomcat 8. I also ran Mongostat to see the details and connection. At start, Mongostat showed 1 connection to MongoDB server. I tested my API using Postman and it was working fine. I then created a load test in SoapUI where I simulated 100 users per second. I saw the update in Mongostat. I saw there were 103 connections. Here is the gif which shows this behaviour.
I am not sure why there are so many connections. The interesting fact is that number of mongo connection are directly proportional to number of users I create on SoapUI. Why is that? I found other similar questions but I think I have implemented there suggestions.
Mongo connection leak with morphia
Spring data mongodb not closing mongodb connections
My code looks like this.
DatabaseConnection.java
// Some imports
public class DatabaseConnection {
private static volatile MongoClient instance;
private static String cloudhost="localhost";
private DatabaseConnection() { }
public synchronized static MongoClient getMongoClient() {
if (instance == null ) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
ServerAddress addr = new ServerAddress(cloudhost, 27017);
List<MongoCredential> credentialsList = new ArrayList<MongoCredential>();
MongoCredential credentia = MongoCredential.createCredential(
"test", "test", "test".toCharArray());
credentialsList.add(credentia);
instance = new MongoClient(addr, credentialsList);
}
}
}
return instance;
}
}
PourService.java
#Secured
#Path("pours")
public class PourService {
final static Logger logger = Logger.getLogger(Pour.class);
private static final int POUR_SIZE = 30;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response createPour(String request)
{
WebApiResponse response = new WebApiResponse();
Gson gson = new GsonBuilder().setDateFormat("dd/MM/yyyy HH:mm:ss").create();
String message = "Pour was not created.";
HashMap<String, Object> data = null;
try
{
Pour pour = gson.fromJson(request, Pour.class);
// Storing the pour to
PourRepository pourRepository = new PourRepository();
String id = pourRepository.createPour(pour);
data = new HashMap<String, Object>();
if ("" != id && null != id)
{
data.put("id", id);
message = "Pour was created successfully.";
logger.debug(message);
return response.build(true, message, data, 200);
}
logger.debug(message);
return response.build(false, message, data, 500);
}
catch (Exception e)
{
message = "Error while creating Pour.";
logger.error(message, e);
return response.build(false, message, new Object(),500);
}
}
PourDao.java
public class PourDao extends BasicDAO<Pour, String>{
public PourDao(Class<Pour> entityClass, Datastore ds) {
super(entityClass, ds);
}
}
PourRepository.java
public class PourRepository {
private PourDao pourDao;
final static Logger logger = Logger.getLogger(PourRepository.class);
public PourRepository ()
{
try
{
MongoClient mongoClient = DatabaseConnection.getMongoClient();
Datastore ds = new Morphia().map(Pour.class)
.createDatastore(mongoClient, "tilt45");
pourDao = new PourDao(Pour.class,ds);
}
catch (Exception e)
{
logger.error("Error while creating PourDao", e);
}
}
public String createPour (Pour pour)
{
try
{
return pourDao.save(pour).getId().toString();
}
catch (Exception e)
{
logger.error("Error while creating Pour.", e);
return null;
}
}
}
When I work with Mongo+Morphia I get better results using a Factory pattern for the Datastore and not for the MongoClient, for instance, check the following class:
public DatastoreFactory(String dbHost, int dbPort, String dbName) {
final Morphia morphia = new Morphia();
MongoClientOptions.Builder options = MongoClientOptions.builder().socketKeepAlive(true);
morphia.getMapper().getOptions().setStoreEmpties(true);
final Datastore store = morphia.createDatastore(new MongoClient(new ServerAddress(dbHost, dbPort), options.build()), dbName);
store.ensureIndexes();
this.datastore = store;
}
With that approach, everytime you need a datastore you can use the one provided by the factory. Of course, this can implemented better if you use a framework/library that support factory pattern (e.g.: HK2 with org.glassfish.hk2.api.Factory), and also singleton binding.
Besides, you can check the documentation of MongoClientOptions's builder method, perhaps you can find a better connection control there.
I am trying to implement a singleton pattern for connecting to my mongodb database so as to make sure i have only one connection.I have written following code '
public enum MongoConnector {
CONNECTION;
private MongoClient client = null;
/**
* This function is used to create a single instance of the MongoDb connector
* Thread Pooling is handled internally by MongoDb
*/
private MongoConnector() {
try {
client = new MongoClient();
} catch (Exception e) {
e.printStackTrace();
}
}
public MongoClient getClient() {
if (client == null) {
throw new RuntimeException();
}
return client;
}
}
So i want to know if this is ensuring the singleton pattern.If not please let me know how it should be .Thank you
Yes, the MongoConnector is a singleton.
I wonder, if there is a way to check if mongoDB server is running from java driver for mongoDB?
According to the tutorial, I can do
Mongo m = new Mongo();
// or
Mongo m = new Mongo( "localhost" , 27017 );
// and
DB db = m.getDB( "mydb" );
But how to check that I can use these Mongo and DB? I see no isConnected() method in the API.
db.getConnector().isOpen()
returns true
The only way I found is call db.getDatabaseNames() and catch MongoException.
If there some more civilized approach?
You can run a ping command
Mongo mongo = new Mongo();
DBObject ping = new BasicDBObject("ping", "1");
try {
mongo.getDB("dbname").command(ping);
} catch (MongoException e) {
...
}
I've found this to be more direct than the ping command:
Mongo mongo = new Mongo();
try {
mongo.getConnector().getDBPortPool(mongo.getAddress()).get().ensureOpen();
} catch (Exception e) {
...
}
if there is a way to check if mongoDB server is running from java driver for MongoDB?
So if you can do the following:
Mongo m = new Mongo( "localhost" , 27017 );
DB db = m.getDB( "mydb" );
Then you are connected to the database, otherwise that m.getDB() would be throwing an exception. If you can connect to the database, then the MongoDB server is running.
The only way I found is call db.getDatabaseNames() and catch MongoException.
If there some more civilized approach?
Is there something specifically wrong with this approach?
The driver basically runs in a sandbox where it can or cannot connect. You're asking the driver to know something specific about the server (is process X running?), but that's not the driver's job. It can either connect or it can't, it's not responsible for operating the service/process, just for connecting to it.
To know that the process is actually running, you need administrative functions on that server that allow you to check that mongod is indeed running with the correct parameters.
public boolean keepAlive(Mongo mongo) {
return mongo.getAddress() != null;
}
This will return null for address if mongo is down. You can look within the implementation of getAddress() to see why it is a good way to check the mongo's status.
I assume you've initialized the mongo parameter properly.
I haven't tested this thoroughly (only using a localhost mongo) but it appears to work so far:
public boolean mongoRunningAt(String uri) {
try {
Mongo mongo = new Mongo(new MongoURI(uri));
try {
Socket socket = mongo.getMongoOptions().socketFactory.createSocket();
socket.connect(mongo.getAddress().getSocketAddress());
socket.close();
} catch (IOException ex) {
return false;
}
mongo.close();
return true;
} catch (UnknownHostException e) {
return false;
}
}
And the tests I've used:
#Test
public void whenMongoNotAvailableAtSpecificURLThenTheLoaderKnows() {
assertThat(mongoRunningAt("mongodb://127.0.0.1:12345"), is(false));
}
#Test
public void whenMongoAvailableAtSpecificURLThenTheLoaderKnows() {
assertThat(mongoRunningAt("mongodb://127.0.0.1:27017"), is(true));
}
It's not exactly using a well defined public API so use at your own risk.
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.
I wonder, if there is a way to check if mongoDB server is running from java driver for mongoDB?
According to the tutorial, I can do
Mongo m = new Mongo();
// or
Mongo m = new Mongo( "localhost" , 27017 );
// and
DB db = m.getDB( "mydb" );
But how to check that I can use these Mongo and DB? I see no isConnected() method in the API.
db.getConnector().isOpen()
returns true
The only way I found is call db.getDatabaseNames() and catch MongoException.
If there some more civilized approach?
You can run a ping command
Mongo mongo = new Mongo();
DBObject ping = new BasicDBObject("ping", "1");
try {
mongo.getDB("dbname").command(ping);
} catch (MongoException e) {
...
}
I've found this to be more direct than the ping command:
Mongo mongo = new Mongo();
try {
mongo.getConnector().getDBPortPool(mongo.getAddress()).get().ensureOpen();
} catch (Exception e) {
...
}
if there is a way to check if mongoDB server is running from java driver for MongoDB?
So if you can do the following:
Mongo m = new Mongo( "localhost" , 27017 );
DB db = m.getDB( "mydb" );
Then you are connected to the database, otherwise that m.getDB() would be throwing an exception. If you can connect to the database, then the MongoDB server is running.
The only way I found is call db.getDatabaseNames() and catch MongoException.
If there some more civilized approach?
Is there something specifically wrong with this approach?
The driver basically runs in a sandbox where it can or cannot connect. You're asking the driver to know something specific about the server (is process X running?), but that's not the driver's job. It can either connect or it can't, it's not responsible for operating the service/process, just for connecting to it.
To know that the process is actually running, you need administrative functions on that server that allow you to check that mongod is indeed running with the correct parameters.
public boolean keepAlive(Mongo mongo) {
return mongo.getAddress() != null;
}
This will return null for address if mongo is down. You can look within the implementation of getAddress() to see why it is a good way to check the mongo's status.
I assume you've initialized the mongo parameter properly.
I haven't tested this thoroughly (only using a localhost mongo) but it appears to work so far:
public boolean mongoRunningAt(String uri) {
try {
Mongo mongo = new Mongo(new MongoURI(uri));
try {
Socket socket = mongo.getMongoOptions().socketFactory.createSocket();
socket.connect(mongo.getAddress().getSocketAddress());
socket.close();
} catch (IOException ex) {
return false;
}
mongo.close();
return true;
} catch (UnknownHostException e) {
return false;
}
}
And the tests I've used:
#Test
public void whenMongoNotAvailableAtSpecificURLThenTheLoaderKnows() {
assertThat(mongoRunningAt("mongodb://127.0.0.1:12345"), is(false));
}
#Test
public void whenMongoAvailableAtSpecificURLThenTheLoaderKnows() {
assertThat(mongoRunningAt("mongodb://127.0.0.1:27017"), is(true));
}
It's not exactly using a well defined public API so use at your own risk.