Testing the database connection with spring and hibernate - java

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.

Related

How to handle exceptions during spring-boot start-up?

This isn't about how to handle exceptions in Spring MVC or anything. I specifically need to handle an exception that can happen while spring is starting, i.e. before the whole application context is even initialised.
For a bit of background, the application in question is an IoT node that allows remote access to electronic equipment. It has a little h2 database built in to persist some data. That data is nice to have at some moments, but not really essential for the application to work.
It so happens that the device the application is running on can get its power cut every once in a while, and if that happens while there was a write operation to the database going on, the file is corrupt and a JdbcSQLException will be thrown when the application tries to boot again.
Since the data is not really essential, the easiest way to make the application work again is to just delete the database and let h2 recreate it. But in order to do that, I have to catch the exception so I can react to it. The application does not have to continue starting, it will be booted up again by systemd. I really just need to identify the exception and delete the file, that's it.
There is one obvious way to do it, which is to put SpringApplication.run in a try-catch block. But it's also really ugly, because I get the exception I'm looking for nested inside a gazillion spring exceptions that were caused by h2 failing to start.
It was also suggested that I catch the exception in the bean that instantiates the database, but unfortunately there is no bean instantiating it. The DB serves as a Quartz job-store and as such is fully managed by spring. Its entire presence in the code are the following entries in the properties file:
spring.quartz.job-store-type=jdbc
spring.quartz.properties.org.quartz.jobStore.misfireThreshold=900000
spring.datasource.name=h2
spring.datasource.url=jdbc:h2:file:${config.folder}controller
spring.datasource.driverClassName=org.h2.Driver
My question is, is there a way to register some kind of exception handler, or other means, to handle the exception directly when it happens, when I can identify it much more easily?
Depends how you've declared the bean. What's wrong with simply wrapping the bean like this?
#Configuration
class Conf {
#Bean
public DB foo() throws JdbcSQLException
{
try
{
return new DB();
}
catch(JdbcSQLException e)
{
deleteDatabase();
throw JdbcSQLException;
}
}
public static void deleteDatabase()
{
//...
}
}

How to add string parameters in lookup method in jdbc connection?

In my web application I'm using JDBC connectivity basically its working fine with below code
connObj = DriverManager.getConnection(dbaseUrl, usrName, Paswrd);
But when I'm using veracode tool its showing flaw as J2EE Bad Practice:getConnection so that i need to implement.
InitialContext ctx= new InitialContext();
DataSource dsrc=(DataSource)ctx.lookup(dbaseUrl, usrName, Paswrd);
dsrc.getConnection();
How can I pass 3 parameters in lookup so that it should not disturb my previous flow of code. Can anybody guide me please?
You cannot change the lookup method parameter. But you can use bind or rebind methods of InitialContext to retrieve your datasource.See sample
ctx.bind("java:/comp/env/jdbc/nameofmyjdbcresource", dsrc);
For details example check here.
A DataSource is a connection to one database and you configure a DataSource with a single username and password. If you really need specific (and changing) usernames/passwords depending on application logic, then you can use DataSource.getConnection(String username, String password). However keep in mind with some (most?) connection pools this will give you a non-pooled connection.
If you want to access a different database (or a different configuration), then you need to specify a data source for each database you want to access and ask for that specific data source.
If that is not possible for your situation, then you should just ignore/suppress the veracode warning and continue using DriverManager.

How do I init a spring integration:gateway at startup?

I'm using spring integration to communicate via active mq. I've got the following config.
<integration:channel id="riskApprovalRequestChannel"/>
<integration:channel id="riskApprovalResponseChannel"/>
<jms:outbound-gateway id="riskApprovalServiceGateway"
request-destination-name="${risk.approval.queue.request}"
reply-destination-name="${risk.approval.queue.response}"
request-channel="riskApprovalRequestChannel"
reply-channel="riskApprovalResponseChannel"
connection-factory="jmsConnectionFactory"
receive-timeout="1000"/>
<integration:gateway id="riskApprovalService" service-interface="com.my.ServiceInterface"
default-request-channel="riskApprovalRequestChannel"
default-reply-channel="riskApprovalResponseChannel"/>
This works well except for the 1st request, which is slow. I always hit the 1 second timeout on the 1st request. There's obviously some lazy loading going on somewhere.
My question is, how do I do a full initialisation at start up to avoid the 1st request always timing out?
Cheers,
Peter
It is probably connection establishment that's the issue.
One thing you can do is wrap the vendor ConnectionFactory in a Spring CachingConnectionFactory (good idea to do this anyway, for performance reasons), and call createConnection() during initialization.
There are a number of ways to do that - including injecting the connection factory into some other bean, with an init-method, #PostConstruct method, or an InitializingBean with afterPropertiesSet(); it will be called during context initialization.
This will cause the cache's connection to be eagerly connected.
You might want to do it in a try block, to avoid the application context failing to initialize because the JMS broker is not available (of course messaging will still fail, but the app will be up and ready to connect when the broker is available).

JDBC Connection Pooling for Servlets

Currently I'm using a separate DBConnectionManager class to handle my connection pooling, but I also realized that this was the wrong way to go as the servlet was not calling the same pool each time a doGet() is performed.
Can someone explain to me why the above is happening?
Is JNDI the way to go for java servlets with tomcat for proper connection pooling?
I have links to 2 articles, is this the correct way to implement connection pooling with servlets?
http://www.javaranch.com/journal/200601/JDBCConnectionPooling.html
http://onjava.com/onjava/2006/04/19/database-connection-pooling-with-tomcat.html
Is it possible to save the db manager object in the context like so:
mtdb = (MTDbManager) context.getAttribute("MTDBMANAGER");
if (mtdb == null) {
System.out
.println("MTDbManager is null, reinitialize MTDbManager");
initMTDB(config);
context.setAttribute("MTDBMANAGER", mtdb);
}
And then I call mtdb.getInstance().getConnection() and it will always reference this object.
Thanks.
Generally, the best advice is to leave the connection pooling to the application server. Just look up the data source using JNDI, and let the application server handle the rest. That makes your application portable (different application servers have different pooling mechanisms and settings) and most likely to be most efficient.
Have a look at, and use, C3P0 instead of rolling your own solution: http://sourceforge.net/projects/c3p0/

tomcat session replication - not serialized exception

I am currently working on one messy web application. In this application, there is a class that holds all the data sources. And whenever the need to connect to a specific data source, the method in the instance of the class is called with a parameter to select the data source.And class is like follows
public class MyConnection implements Runnable,DbConnection, Serializable
{
private static final long serialVersionUID=2007L;
public static transient DataSource FirstDatasource;
public static transient DataSource SecondDatasource;
BaseDbConnection _bidc;
....
And on each page, this object is to get and set to the session(I don't know why like this). And it works fine with the current setup. (clustering, load balancing etc..)
But my work is to implement fail-over, and when I enable session replication(in memory - simple TCP), writing the session is failing and its throwing the following exception
org.apache.catalina.ha.session.DeltaManager requestCompleted
SEVERE: Unable to serialize delta request for sessionid [FE02AF01C76F41D042FE04692462D983.tomcat1]
java.io.NotSerializableException: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1081)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1375)
.....
Since there are more than "I can count" no of pages, it's not easy to replace the code which sets the connection object in session from each and every page. And the complete app is based on this connection object(Datasources also play an important role).
Is there a way I can change this class so that it can be persisted in session?
If I understood correctly, I would say that replicating a DataSource is not correct, it can not work.
What need to be done is, after deserialing, to get a new (local) DataSource that corresponds to the need, and set it in the field. It is possible that this happens already in your code, look for a readResolve method.
If some parameters are needed to know which DataSource, they can be serialized themselves (because they are no DataSource, they could be just Strings for example).

Categories