How to use one instance of MongoClient in java application server - java

According to mongodb java concurrency driver we can use one instance of MongoClient for multiple threads for example inside application servers. The only way I know to do this is to create MongoClient in static block:
static {
MongoClient mongoClient = new MongoClient("localhost", 27017);
}
the problem is I can't catch MongoException and return some helpfull information to user. So how to share a single instance of MongoClient between multiple threads inside Java EE application servers?

You can do one of the following:
Create a service class and initiate the mongo connection lazily on first request, showing an error when you fail
Add a try catch and remember the error statically (I really don't like this one! But better than failing on exception in static context)
Use spring to initialize mongo (my preferred option)

Related

MongoDB: IllegalStateException: The pool is closed error message in Spring Boot application

I'm getting this info message:
19-October-24 08:05:53:481 INFO Thread-4 o.m.d.connection:71 - Closed
connection [connectionId{localValue:35, serverValue:38}] to
mongodb:27017 because the pool has been closed.
And also the following errors:
java.lang.IllegalStateException: The pool is closed at
com.mongodb.internal.connection.ConcurrentPool.get(ConcurrentPool.java:137)
at
java.lang.IllegalStateException: state should be: open at
com.mongodb.assertions.Assertions.isTrue(Assertions.java:70)
This is how I'm creating the MongoClient, plain and simple:
#Bean
#Override
public MongoClient mongoClient() {
return new MongoClient(host);
}
This SO answer suggests to set socketKeepAlive(true) but as far as I understand this method is deprecated as it's true by default.
I'm using MongoTemplate and MongoRepository.
The application (as mentioned above) is multi-threaded.
I'd like to understand what's the meaning of the error ? (i.e. Why would the pool ever be closed?).
Do I need to set/adjust some Spring-Boot parameters maybe? Do I need to build MongoClient differently?
This error means your MongoDB connections are closed for some reason and you are trying to use this connection pool.
If you are using springs connection pool you can create your connection pool without spring and you can manage the connection when is closed. ( Like reconnection on errors )
If you are doing multithreaded operations change your MongoClient beans Scope and create it thread based. Mongoclient creates a connection pool in the background and gives already pooled connections to newly created clients so thread-based clients will not create connection on every autowire operation.
If you want to use the socketKeepAlive feature you need to give options like this:
MongoClientOptions options = MongoClientOptions.builder()
.socketKeepAlive(false)
.build();
MongoClient mongoClient = new MongoClient( "yourhost:mongoport" , options);

How to create and maintain one jdbc connection per user in web app (Spring/Vaadin)?

How to create an application which can handle thousand of jdbc connection at runtime without implementing connection pool ? AFAIK to establish connection pool, we need username, passowrd and required dbinstance url but here all of them will be provided at runtime to connect particular database, and there would be more than 1000 user at one time to connect to set of databases.(memory intensive !)
So typically it going to be like this:
Users: User-A,User-B,User-C.....User-n
db: DB1, DB2, DB3....DBn
Can anyone please guide me how can I achieve this task ?
I only have one thing in my mind, i.e. to create single connection with each session and use it whereever required specific to that user.
I've used Apache Commons DBCP2 for connection pooling, MyBatis-Spring implementation, Spring and Vaadin for different application but not sure if anyone of them gonna help me !
Here's another approach:
Oracle supports proxy authentication. It would work something like this:
setup limited rights user for your application (say webgui)
connect to database as webgui (w connection pooling)
authenticate the real user (say JoeSmith) by simply trying to connect as him (JoeSmith/password), perhaps w a second connection
in first connection change user to JoeSmith (not sure what oracle syntax is, in postgres it's SET ROLE)
reset user at end of database session
EclipseLink has a postAcquireClientSession method, not sure about MyBatis
You might have to wipe any caching in your ORM if it uses it
Finally, I had to settle down with following approach. Though I am not sure if its a good approach.
I created a SqlSessionFactory by providing DataSource with dynamic Username, Password and Database.
public SqlSessionFactory build() throws IOException, SQLException
{
OracleDataSource dataSource = new OracleDataSource();
dataSource.setURL(this.dbUrl);
dataSource.setUser(this.dbUsername);
dataSource.setPassword(this.dbPassword);
dataSource.setDriverType(properties.getProperty("db.driver"));
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment(properties.getProperty("db.environment"), transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMappers("com.app.dao");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configuration);
// final test connection to db
sessionFactory.openSession().getConnection();
return sessionFactory;
}
Then I am getting one SqlSession out of factory:
SqlSession session = sessionFactory.openSession();
and I am setting it across Vaadin session :(, so that it would be available throughout session. Hence I can use it whenever I need by taking it from session.
UI.getCurrent().getSession().setAttribute(SqlSession.class,session);
I am discarding it when logout:
UI.getCurrent().getSession().setAttribute(SqlSession.class, null);
I feel its dirty and may create memory issue. but didn't find any other easy solution. Please feel free to comment or answer.

Java MongoDB Exception: can't authenticate twice on the same database

I've got a problem with MongoDB User Authentication using the Java driver 2.11.1. I added some admin-users (dbAdmins, userAdmins etc.) to the admin database.
As suggested by the MongoDB tutorial, I use only one instance of the MongoClient object: it's implemented as singleton because the MongoClient object is like an connection pool.
If an admin wants to have access to the database, he will make up a new connection to the database with the global MongoClient instance (get one connection from the pool) and call the authentication method of the driver: an example:
MongoClient mongoClient = new MongoClient("ip", "port");
DB adminDB = mongoClient.getDB("admin");
boolean isAuth = adminDB.authenticate("Admin", "Admin1234".toCharArray());
DB anotherAdminDB = mongoClient.getDB("admin");
boolean isAuth2 = anotherAdminDB.authenticate("UserAdmin", "UserAdmin1234".toCharArray());
If I do so, I will get the exception:
java.lang.IllegalStateException: can't authenticate twice on the same
database
But each admin user has to authenticate with it's own credentials. Does anyone had this problem already? How could you solve that problem?
If I create a new MongoClient per admin, then there is no exception thrown and all is correct. But then I can't use the connection pooling of Mongo.
Thank you and best regards.
I think that limitation is by design. Maybe mongodb authentication is more appropriate in the context of application <-> database, not from user <-> application. If you need to authenticate user access to your application, better to implement it your own (eg: using Java EE / Spring Security)

Testing the database connection with spring and hibernate

I'm currently working on a java application. It's a standalone client with Spring and Hibernate. Also C3P0.
In the previous version we used a standard user(hardcoded in the configuration file) for the database connection but now we changed it so that every user has to provide his own credentials.
The beans with the code for the database are basically created on-demand.
I changed the XML-files and added a postprocessor which sets the credentials as well as some connection settings. It looks similar to this now:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
configurer = new PropertyPlaceholderConfigurer();
// properties are retrieved from a private method
configurer.setProperties(getProperties());
context.addBeanFactoryPostProcessor(configurer);
context.setConfigLocations(new String[] { "first.xml","second.xml" });
context.refresh();
return context.getBean("myClass", MyClass.class);
This all works as expected but now we reach the part where I'm currently stuck.
I want to provide a test functionality for the connection, so that the user can click a button and then is told if his credentials were accepted or not.
My first idea was to do a simple select on the database. Sifting through the logs however, I noticed that Spring tries to connect to the database during the refresh() (or rather the instantiation of the bean) anyway. I can see exceptions in the logs, for example: java.sql.SQLException: Connections could not be acquired from the underlying database!
Unfortunately, Spring doesn't seem to actually care. The exceptions are logged away but refresh() finishes and is not throwing any exceptions because of this. I had hoped that I could simply catch the exception and then I would know that the connection failed.
I could just do the select as planned, but I want to limit the connection attempts as much as possible, because the database server will block the user after several attempts. Even permanently if there are to many attempts(already had some fun with that, before I changed the settings for the connection pool).
My searches regarding this problem came up with practically nothing. Is there a way to get the exception somehow? Or does Spring provide an API of sorts that would tell me about the connection error during the instantiation/refresh?
Failing that, any ideas for an alternative approach? Preferably one that needs only a single attempt to determine if a connection is possible.
Edit: For anyone interested: I went with the suggestion from Santosh and implemented a connection test in JDBC.
Unfortunately there seems to be no easy way to make use of the database errors/exceptions encountered during the bean instantiation.
The kind of functionality you are looking for would be very tricky to accomplish using spring+hibernate.
The connection properties are set at the session-factory level and if credentials are incorrect, the session-factory is not instantiated.
Quoting #Bozo from his answer here.
What you can do is extend LocalSessionFactoryBean and override the
getObject() method, and make it return a proxy (via
java.lang.reflect.Proxy or CGLIB / javassist), in case the
sessionFactory is null. That way a SessionFactory will be injected.
The proxy should hold a reference to a bare SessionFactory, which
would initially be null. Whenever the proxy is asked to connect, if
the sessionFacotry is still null, you call the buildSessionFactory()
(of the LocalSessionFactoryBean) and delegate to it. Otherwise throw
an exception. (Then of course map your new factory bean instead of the
current)
There is also a simple and rudimentary approach wherein before creating ClassPathXmlApplicationContext, simply try to obtain a connection using raw JDBC calls. If that succeed then proceed or else give use appropriate message.
You can limit the connection attempts here as you are in full control.

MongoDB configuration in a java web app

I'm looking for some advice on the proper way to set up mongoDB for my web application that runs with java.
From the mongoDB tutorial, i understand that I should have only one instance of the Mongo class.
The Mongo class is designed to be thread safe and shared among threads. Typically you create only 1 instance for a given DB cluster and use it across your app.
So I've got a singleton provider for this (I'm using guice for injection)
#Singleton
public class MongoProvider implements Provider<Mongo> {
private Mongo mongo;
public Mongo get() {
if (mongo == null)
mongo = new Mongo("localhost", 27017);
return mongo;
}
}
And whenever I have to work with mongo in my webapp i inject the provider and get the same instance of mongo.
public class MyService {
private Provider<Mongo> mongoProvider;
#Inject
private MyService(Provider<Mongo> mongoProvider) {
this.mongoProvider = mongoProvider;
}
public void execute() {
DB db = mongoProvider.get().getDB("mydatabase");
DBCollection coll = db.getCollection("mycollection");
// Do stuff in collection
...
}
}
What I find weird is that everytime i access my database, i get logs like this from mongo :
[initandlisten] connection accepted from 192.168.1.33:54297 #15
[initandlisten] connection accepted from 192.168.1.33:54299 #16
So far, I haven't had any problems but I'm wondering if it's good practice and if I won't run into any problems when the number of connections accepted gets too high.
Should I also have only one instance of the DB object for my entire app ?
Do I have to configure MongoDB differently to automatically close the connections after some time ? Or do I have to close connections manually ? I've read something about using the close() method on Mongo but I'm not sure when or if to call it.
Thank you for you advice.
This is good practice. Each instance of Mongo manages a connection pool, so you will see multiple connections in the mongod logs, one for each connection in the pool. The default pool size is 10, but that can be configures using the connectionsPerHost field in MongoOptions.
Mongo instances also maintain a cache of DB instances, so you don't have to worry about maintaining those as singletons yourself.
You do not have to configure Mongo to automatically close connections. You can call Mongo#close at the appropriate time to close all the sockets in the connection pool.
Founded something like this om MondoDB site:
"The Java MongoDB driver is thread safe. If you are using in a web serving environment, for example, you should create a single MongoClient instance, and you can use it in every request. The MongoClient object maintains an internal pool of connections to the database (default pool size of 10). For every request to the DB (find, insert, etc) the Java thread will obtain a connection from the pool, execute the operation, and release the connection. This means the connection (socket) used may be different each time."
And from FAQ from MongoSite which I think completely anwsers on you question.
http://docs.mongodb.org/manual/faq/developers/#why-does-mongodb-log-so-many-connection-accepted-events

Categories