So pre spring, we used version of HibernateUtil that cached the SessionFactory instance if a successful raw JDBC connection was made, and threw SQLException otherwise. This allowed us to recover from initial setup of the SessionFactory being "bad" due to authentication or server connection issues.
We moved to Spring and wired things in a more or less classic way with the LocalSessionFactoryBean, the C3P0 datasource, and various dao classes which have the SessionFactory injected.
Now, if the SQL server appears to not be up when the web app runs, the web app never recovers. All access to the dao methods blow up because a null sessionfactory gets injected. (once the sessionfactory is made properly, the connection pool mostly handles the up/down status of the sql server fine, so recovery is possible)
Now, the dao methods are wired by default to be singletons, and we could change them to prototype. I don't think that will fix the matter though - I believe the LocalSessionFactoryBean is now "stuck" and caches the null reference (I haven't tested this yet, though, I'll shamefully admit). This has to be an issue that concerns people.
Tried proxy as suggested below -- this failed
First of all I had to ignore the suggestion (which frankly seemed wrong from a decompile) to call LocalSessionFactory.buildSessionFactory
- it isn't visible.
Instead I tried a modified version as follows:
override newSessionFactory. At end return proxy of SessionFactory
pointing to an invocation handler listed below
This failed too.
org.hibernate.HibernateException: No local DataSource found for configuration - 'dataSource' property must be set on LocalSessionFactoryBean
Now, if newSessionfactory()
is changed to simply
return config.buildSessionFactory()
(instead of a proxy) it works, but of course no longer exhibits the desired proxy behavior.
public static class HibernateInvocationHandler implements InvocationHandler {
final private Configuration config;
private SessionFactory realSessionFactory;
public HibernateInvocationHandler(Configuration config) {
this.config=config;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (false) proxy.hashCode();
System.out.println("Proxy for SessionFactory called");
synchronized(this) {
if (this.realSessionFactory == null){
SessionFactory sf =null;
try {
System.out.println("Gonna BUILD one or die trying");
sf=this.config.buildSessionFactory();
} catch (RuntimeException e) {
System.out.println(ErrorHandle.exceptionToString(e));
log.error("SessionFactoryProxy",e);
closeSessionFactory(sf);
System.out.println("FAILED to build");
sf=null;
}
if (sf==null) throw new RetainConfigDataAccessException("SessionFactory not available");
this.realSessionFactory=sf;
}
return method.invoke(this.realSessionFactory, args);
}
}
The proxy creation in newSessionFactory looks like this
SessionFactory sfProxy= (SessionFactory) Proxy.newProxyInstance(
SessionFactory.class.getClassLoader(),
new Class[] { SessionFactory.class },
new HibernateInvocationHandler(config));
and one can return this proxy (which fails) or config.buildSessionFactory() which works but doesn't solve the initial issue.
An alternate approach has been suggested by bozho, using getObject(). Note the fatal flaw in d), because buildSessionFactory is not visible.
a) if this.sessionfactory is nonnull, no need for a proxy, just return b) if it is , build a proxy which... c) should contain a private reference of sessionfactory, and each time it is called check if it is null. If so, you build a new factory and if successful assign to the private reference and return it from now on. d) Now, state how you would build that factory from getObject(). Your answer should involve calling buildSessionFactory....but you CAN'T. One could create the factory by oneself, but you would end up risking breaking spring that way (look at buildSessionFactory code)