views:

227

answers:

3

Hi All, Actually, This is not a question but really I need your opinions in a matter... I put his post here because I know you always active, so please don't consider this a bad question and share me your opinions.

I've used Java dynamic proxies to Centralize The code of JPA that I used in a standalone mode, and Here's the dynamic proxy code:

package com.forat.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import com.forat.service.exceptions.DAOException;

/**
 * Example of usage :
 * <pre>
 * OnlineFromService onfromService = 
 *            (OnlineFromService) DAOProxy.newInstance(new OnlineFormServiceImpl());
 *        try {
 *            Student s = new Student();
 *            s.setName("Mohammed");
 *            s.setNationalNumber("123456");
 *            onfromService.addStudent(s);    
 *        }catch (Exception ex) {
 *            System.out.println(ex.getMessage());
 *        }
 *</pre>
 * @author mohammed hewedy
 *
 */
public class DAOProxy implements InvocationHandler{

    private Object object;
    private Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    private DAOProxy(Object object) {
        this.object = object;
    }

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        EntityManagerFactory emf = null;
        EntityManager em = null;
        EntityTransaction et = null;
        Object result = null;
        try {
            emf = Persistence.createEntityManagerFactory(Constants.UNIT_NAME);
            em = emf.createEntityManager();;
            Method entityManagerSetter = object.getClass().
                getDeclaredMethod(Constants.ENTITY_MANAGER_SETTER_METHOD, EntityManager.class);
            entityManagerSetter.invoke(object, em);
            et = em.getTransaction();
            et.begin();
            result = method.invoke(object, args);
            et.commit();
            return result;
        }catch (Exception ex) {
            et.rollback();
            Throwable cause = ex.getCause();
            logger.log(Level.SEVERE, cause.getMessage());
            if (cause instanceof DAOException)
                throw new DAOException(cause.getMessage(), cause);
            else
                throw new RuntimeException(cause.getMessage(), cause);
        }finally {
            em.close();
            emf.close();
        }
    }
}

And here's the link that contains more info (http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html) (plz don't consider this as adds, as I can copy and past the entire topic here if you want that)

So, Please give me your opinions.

Thanks.

A: 

Thanks all. Done

Mohammed
A: 

I've used something similar in the past, but coded to the hibernate API (this was pre-JPA). Data access for most types of DAO was managed by an interface named after the object type, E.g. CustomerPersistence for managing Customer instnaces. Methods such as findXXX mapped to named queries, with parameter names in the method mapped to parameters in the query.

The implementation of the interfaces were proxies, which used the interface name, method names, parameter names etc.. to invoke appropriate methods in the hibernate API.

It saves a lot of boilerplate coding, with an intuitive mapping to the underlying data access framework, and makes for very easy mocking of the data access layer.

So, I'm definitely "thumbs up" on using proxies.

mdma
Thanks for your comment.
Mohammed
+1  A: 

So you've encapsulated the transaction demarcation logic in one place and use dynamic proxy to enhance existing services with transaction management and reduce boilerplate code, right?

The sounds a rather OK to me. Actually what containers such as Spring, or EJB do when we speak of declarative transaction demarcation is very similar. Implementation-wise, you can do it with dynamic proxy, or byte code instrumentation, or even use AspectJ. I did something very similar once for a tiny testing framework once. Here is a blog post about it.

The tricky parts that I see are:

1) Rollback only. As per JPA spec, an entity transaction can be flagged as "rollback only". Such a transaction can never commit. So I feel like you should check that between these two lines:

result = method.invoke(object, args);
et.commit();

2) Re-entrancy. Most system that have declarative transaction implement a semantics in which a transaction is started only if there isn't one already active (See "Required" in this list of EJB annotations). Looks like you should maybe check with isActive that in your logic.

3) Exception handling. Be very careful with the exception propagation in dynamic proxy. The proxy is supposed to be transparent for the client as much as possible. If an exception other than DAOException leaks out of the DAO, the proxy will transform it into a RuntimeException. Doesn't sound optimal to me. Also don't confuse the exception because invoke failed, and the exception wrapped by the invocation, that I think you should re-throw as-is:

catch ( InvocationTargetException e )
{
     Throwable nested = e.getTargetException();
     throw nested;
}

Conclusion: the idea to use dynamic proxy in this scenario sounds OK to me. But I suspect there are a few stuffs to double-check in your code (I don't remember all the details of the JPA specs and exception handling with dynamic proxy, but there are some tricky cases). This kind of code can hide subtle bugs, so it's worth taking time to make it bullet-proof.

ewernli
If you don't mind, I've copied your valuable reply to my blog at:http://m-hewedy.blogspot.com/2010/04/using-dynamic-proxies-to-centralize-jpa.html
Mohammed
BTW, I just mentioned it.
Mohammed