Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a standard or easy way to freeze a JavaBean?

Tags:

java

javabeans

I find myself in the unfortunate position of using a lot of JavaBeans. I say unfortunate because I don't like that they are mutable. So after I create the beans (I am converting them from JSON using Jackson), I would like to "freeze" the beans at a specified moment to ensure they aren't mutated down the line.

I don't have the capacity to write Mutable/Immutable versions of each and every bean, nor custom Jackson factories, so I'm looking for a library or function call much like Collections.unmodifiableCollection(...); but for beans.

(Yes I know that is not truly freezing it if the original bean gets leaked. But I'll wrap or freeze them right after creation and never use the original again.)

Any ideas?

like image 653
Michael Deardeuff Avatar asked Oct 20 '12 00:10

Michael Deardeuff


2 Answers

Are you beans interface based or pojos ? If they're interface based, you could simply write a utility method:

public class ImmutableBean {
    public static <T> T makeImmutable(T t) { ... }
}

You could then create a java.lang.Proxy instance which would either be specific to your bean, or look for patterns like methods starting with "set", and throw some particular exception. Here's a good short tutorial on creating a proxy instance: http://www.javalobby.org/java/forums/t18631.html

If they are pojo's, you should be able to use a library like javassist or cglib to create an enhanced version where your setters are stubbed to throw an exception.

Here's a javassist example:

 public static <T> T makeImmutable(final T t) throws NotFoundException, InstantiationException, IllegalAccessException {
    MethodHandler mh = new MethodHandler() {
        @Override
        public Object invoke(java.lang.Object self, java.lang.reflect.Method thisMethod, java.lang.reflect.Method proceed, java.lang.Object[] args) throws Throwable {
            if (thisMethod.getName().startsWith("set")) {
                throw new UnsupportedOperationException();
            }
            return proceed.invoke(t, args);
        }

    };

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setSuperclass(t.getClass());
    Class<T> clazz = (Class<T>) proxyFactory.createClass();
    T instance = clazz.newInstance();
    ((ProxyObject)instance).setHandler(mh);

    return instance;

}

This does however, this will require that your original class have a no-arg constructor, so you might have to do a specific method for your class if you don't have a no arg constructor.

like image 69
Matt Avatar answered Oct 21 '22 07:10

Matt


Google found this when I searched on "java beans jackson immutable":

http://www.cowtowncoder.com/blog/archives/2010/08/entry_409.html

I don't know if it works, but the article claims to be able to do it.

like image 3
duffymo Avatar answered Oct 21 '22 07:10

duffymo