Friday, 22 August 2008

Managing multiple databases transparently over Hibernate

The JBlooming platform supports “Managing multiple databases transparently over Hibernate”; this allows also in case of multiple databases to keep using the pages and web components without worrying about Hibernate sessions and transactions. The technique is explained on Hibernate’s wiki, here:

Here we provide some sample code, specific to JBlooming framework. The web.xml file should poit to two configuration files, comma separated.

ThreadLocalPersistenceContextCarrier.java

package org.jblooming.persistence;

...

public class ThreadLocalPersistenceContextCarrier {

private Operator operator;
public PersistenceContext currentPC;

public Map
persistenceContextMap=new Hashtable();

...

PersistenceContext.java

package org.jblooming.persistence.hibernate;
...

public class PersistenceContext {

public Session session;
public PersistenceConfiguration persistenceConfiguration;

public static ThreadLocal threadLocalPersistenceContextCarrier = new ThreadLocal() {

protected ThreadLocalPersistenceContextCarrier initialValue() {
return null;
}
};
________________________________________

public PersistenceContext() {
this((Connection) null);
}
________________________________________

public PersistenceContext(Connection c) {
this(null, c);
}
________________________________________

/**
* this is not managed by the filter
*/
public PersistenceContext(String persistenceConfigurationName, Connection c) {

if (JSP.ex(persistenceConfigurationName))
persistenceConfiguration = PersistenceConfiguration.persistenceConfigurations.get(persistenceConfigurationName);
else
persistenceConfiguration = PersistenceConfiguration.getFirstPersistenceConfiguration();

Sniffer interceptor = null;
if (Fields.TRUE.equals(ApplicationState.getApplicationSetting(SystemConstants.AUDIT)))
interceptor = new Sniffer();

Throwable t = null;
try {
if (c != null && interceptor != null)
session = persistenceConfiguration.getSessionFactory().openSession(c, interceptor);
else if (c != null)
session = persistenceConfiguration.getSessionFactory().openSession(c);
else if (interceptor != null)
session = persistenceConfiguration.getSessionFactory().openSession(interceptor);
else
session = persistenceConfiguration.getSessionFactory().openSession();

session.beginTransaction();

} catch (Throwable e) {
t = e;
}
//by thowing the exception only if there is an exp AND session is null, in all other cases the external finally can close the session correctly
if (t != null && session == null)
throw new PlatformRuntimeException(t);
}
________________________________________
public PersistenceContext(Session session) {
this.session = session;
}
________________________________________
public static Connection getNewConnection() throws ClassNotFoundException, SQLException {
PersistenceConfiguration pcf = PersistenceConfiguration.getDefaultPersistenceConfiguration();
Class.forName(pcf.driver_class);
return DriverManager.getConnection(pcf.driver_url, pcf.db_user_name, pcf.db_user_psw);
}
________________________________________
private void close(boolean thereHasBeenAnException) throws PersistenceException {

boolean exceptionInCommit = true;
if (!thereHasBeenAnException) {

try {
if (session.getTransaction() != null) {
session.getTransaction().commit();
exceptionInCommit = false;
}
} catch (Throwable throwable) {
Tracer.platformLogger.error(throwable);
}
}

if (thereHasBeenAnException || exceptionInCommit) {
try {
if (session.getTransaction() != null) {
session.getTransaction().rollback();
}
} catch (Throwable throwable) {
Tracer.platformLogger.error(throwable);
}
}

try {
//as we are NOT using Hibernate managed context, in contrast with FrontControllerFilter, there is need after commit/rollbacdk to close session
session.close();
} catch (Throwable throwable) {
throw new PersistenceException(throwable);
}
}
________________________________________

public void checkPoint() {
if (session.isOpen()) {
Transaction currentTransaction = session.getTransaction();
if (session.isOpen() && currentTransaction != null && !currentTransaction.wasRolledBack()) {
session.flush();
currentTransaction.commit();
session.beginTransaction();
}
}
}
________________________________________
public void commitAndClose() throws PersistenceException {
close(false);
}
________________________________________
public void rollbackAndClose() {
try {
close(true);
} catch (PersistenceException e) {
}
}
________________________________________
public static PersistenceContext getDefaultPersistenceContext() {
return switchTo((String) null);
}
________________________________________

public static PersistenceContext switchToFirst() {
return switchTo(PersistenceConfiguration.getFirstPersistenceConfiguration().name);
}
________________________________________

public static PersistenceContext switchTo(Class persistentClass) {
PersistenceContext persistenceContext = get(persistentClass);
threadLocalPersistenceContextCarrier.get().currentPC = persistenceContext;
return persistenceContext;
}
________________________________________

public static PersistenceContext switchTo(String persistenceConfigurationName) {
// set last used as current
PersistenceContext pc = getByConfigurationName(persistenceConfigurationName);
threadLocalPersistenceContextCarrier.get().currentPC = pc;
return pc;
}
________________________________________

public static PersistenceContext get(Class persistentClass) {
String conf = null;
for (String confName : PersistenceConfiguration.persistenceConfigurations.keySet()) {
PersistenceConfiguration pcf = PersistenceConfiguration.persistenceConfigurations.get(confName);
if (pcf.getHibernateConfiguration().getClassMapping(persistentClass.getName()) != null) {
conf = confName;
break;
}
}
return getByConfigurationName(conf);
}
________________________________________

public static PersistenceContext get(IdentifiableSupport persistentObj) {
String className=PersistenceHome.deProxy(persistentObj.getClass().getName());
String conf = null;
for (String confName : PersistenceConfiguration.persistenceConfigurations.keySet()) {
PersistenceConfiguration pcf = PersistenceConfiguration.persistenceConfigurations.get(confName);
if (pcf.getHibernateConfiguration().getClassMapping(className) != null) {
conf = confName;
break;
}
}
return getByConfigurationName(conf);
}
________________________________________

public static PersistenceContext get(String className) {
PersistenceContext pc;
try {
pc = PersistenceContext.get((Class) Class.forName(className));
} catch (ClassNotFoundException e) {
throw new PlatformRuntimeException(e);
}
return pc;
}
________________________________________

private static PersistenceContext getByConfigurationName(String persistenceConfigurationName) {
//there could be already a closed session on it

ThreadLocalPersistenceContextCarrier localPersistenceContextCarrier = null;
// exists ThreadLocal?
if (threadLocalPersistenceContextCarrier.get() == null) {
localPersistenceContextCarrier = new ThreadLocalPersistenceContextCarrier();
threadLocalPersistenceContextCarrier.set(localPersistenceContextCarrier);
}
localPersistenceContextCarrier = threadLocalPersistenceContextCarrier.get();

// guess the right persistenceConfiguration Name
PersistenceConfiguration persistenceConfiguration;
if (JSP.ex(persistenceConfigurationName))
persistenceConfiguration = PersistenceConfiguration.persistenceConfigurations.get(persistenceConfigurationName);
else {
// if there is a current in use sedimented on threadlocal, give that
if (localPersistenceContextCarrier.currentPC != null)
persistenceConfiguration = localPersistenceContextCarrier.currentPC.persistenceConfiguration;
//no current sedimented on threadlocal, use the first, "main" one
else
persistenceConfiguration = PersistenceConfiguration.getFirstPersistenceConfiguration();
}

// exist a PersistentContext for this conf ?
PersistenceContext persistenceContext = localPersistenceContextCarrier.getPersistenceContext(persistenceConfiguration.name);
if (persistenceContext == null) {
persistenceContext = new PersistenceContext(persistenceConfigurationName, null);
localPersistenceContextCarrier.putPersistenceContext(persistenceContext);
System.out.println("Creating new pc thlocid:"+localPersistenceContextCarrier.hashCode());
} else {
// check if sessions are still good
if (persistenceContext.session != null && !(persistenceContext.session.isOpen())) {
persistenceContext = new PersistenceContext(persistenceConfigurationName, null);
localPersistenceContextCarrier.putPersistenceContext(persistenceContext);
System.out.println("Creating currupted new pc thlocid:"+localPersistenceContextCarrier.hashCode());
}
}
return persistenceContext;
}
}