16 July 2012

Using Dynamic Proxies to Manage Transactions to DB (3)

This is the third post and the more mature one in the same topic, Please see :

Post 1: http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html
Post 2: http://m-hewedy.blogspot.com/2012/06/using-dynamic-proxies-to-centralize.html

The following code is the more mature (but still have a bug see: http://stackoverflow.com/questions/11565022/setting-instance-variables-of-the-proxied-objects-in-the-invocationhandler#comment15299502_11565022 )


please see http://stackoverflow.com/questions/11506159/is-there-a-limit-for-maximum-number-of-concurrent-transactions-in-hibernate

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;


/**
 * http://stackoverflow.com/questions/2587702
 * 
 * @author mohammad_abdullah
 */
public class ServiceProxy implements InvocationHandler {

 private final Object object;
 private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());
 private static final String SESSION_FACTORY_FIELD = "sessionFactory";

 public static final Map ACTIVE_TRANSACTIONS = new HashMap();

 private ServiceProxy(final Object object) {
  this.object = object;
 }

 public static Object newInstance(final Object object) {
  return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), new ServiceProxy(object));
 }

 @Override
 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {

  Object result = null;
  SessionFactory sessionFactory = null;
  boolean isActive = false;
  try {
   if (Modifier.isPublic(method.getModifiers())) {

    sessionFactory = HibernateUtil.getSessionFactory();

    final Field sessionField = this.object.getClass().getSuperclass().getDeclaredField(SESSION_FACTORY_FIELD);
    if (sessionField == null) {
     throw new UPSAdminException("Service Implementation should have field named: \"" + SESSION_FACTORY_FIELD + "\".");
    }
    sessionField.setAccessible(true);
    sessionField.set(this.object, sessionFactory);

    isActive = sessionFactory.getCurrentSession().getTransaction().isActive();

    if (!isActive) {
     this.logger.info("Tnx begin." + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
     final Transaction newTnx = sessionFactory.getCurrentSession().beginTransaction();
     ACTIVE_TRANSACTIONS.put(this.threadId(), newTnx);
    } else {
     this.logger.info("Joing Active tnx" + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    }
    result = method.invoke(this.object, args);

    if (!isActive) {
     ACTIVE_TRANSACTIONS.remove(this.threadId()).commit();
     this.logger.info("Tnx commit" + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    }
   } else {
    result = method.invoke(this.object, args);
   }

   return result;

  } catch (final InvocationTargetException _ex) {
   final Throwable cause = _ex.getCause();
   this.logger.severe("Caller Exept: " + cause + " {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");

   if (!isActive && (sessionFactory.getCurrentSession() != null) && sessionFactory.getCurrentSession().getTransaction().isActive()) {
    ACTIVE_TRANSACTIONS.remove(this.threadId()).rollback();
   }

   if (cause instanceof HibernateException) {
    this.logger.severe("Hibernate Error. Rollback. {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    throw new DBException(cause.getCause().getMessage());

   } else if (cause instanceof SetRollbackException) {
    this.logger.severe("Tnx marked rollback. {Method: " + method.getName() + "} {Thread: " + this.threadId() + "}");
    return result;

   } else {
    this.logger.severe("Error in Business Method : " + method + ". Rollbacked Back." + " Thread: " + this.threadId() + "}");
    throw cause;
   }
  } catch (final Exception ex) {
   this.logger.severe("Error in Proxy code :" + ex + " {Mehtod :" + method + "} {Thread: " + this.threadId() + "}");

   if (!isActive && (sessionFactory.getCurrentSession() != null) && sessionFactory.getCurrentSession().getTransaction().isActive()) {
    ACTIVE_TRANSACTIONS.remove(this.threadId()).rollback();
   }

   if (ex instanceof HibernateException) {
    throw new DBException(ex.getCause().getMessage());
   }

   throw ex;
  }
 }

 private long threadId() {
  return Thread.currentThread().getId();
 }
}

No comments: