<code>
public class Pojo {
private String name;}
public void setName(String name) { this.name = name; }
public String getName() { return name; }
</code>
Then, wrap the POJO:
<code>
Pojo javaBean = JavaBeanAdapter.wrap(new Pojo());</code>
That's all. I usually do this in my projects that use JGoodies Binding.
<code>
PresentationModel model = new PresentationModel(JavaBeanAdapter.wrap(new Pojo());</code>
Here is the complete source code for JavaBeanAdapter.
<code>
package com.katalis.commons.util;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.apache.commons.beanutils.PropertyUtils;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Java Bean adapter using CGLIB. Adapted from <a href="http://cglib.sourceforge.net/xref/samples/Beans.html">http://cglib.sourceforge.net/xref/samples/Beans.html</a>.
* Adapt POJO to meet JavaBean specification. All generated objects will implement {@link Observable}.
* <p/>
* Expected use of this adapter is as follow:<pre>
* Foo javaBean = JavaBeanAdapter.wrap(new Foo());
* </pre>
* <p/>
* All object graph of the pojo will be wrapped. Exceptions are those of standard java packages.
* <p/>
* Note: Great care must be excercised if you modify the pojo after wrapping. The most obvious is that
* the symetric contract of {@link Object#equals(Object)} will be violated. Example:<pre>
* javaBean.setFoo("foo");
* pojo.setFoo("another");
* javaBean.equals(pojo) //true
* pojo.equals(javaBean) // false
* </pre>
* The safest approach is to use the generated java bean instead.
*
* @author Thomas Edwin Santosa
*/
public class JavaBeanAdapter {
// ------------------------------ FIELDS ------------------------------
private static final Logger log = Logger.getLogger(JavaBeanAdapter.class.getPackage().getName());
private static final Map cache = Collections.synchronizedMap(new WeakHashMap());
private static final InternalCallbackFilter INTERNAL_CALLBACK_FILTER = new InternalCallbackFilter();
private static final NamingPolicy namingPolicy = new JBNamingPolicy();
private static final Class[] INTERFACES = new Class[]{Observable.class};
private Object pojo;
private final PropertyChangeSupport propertySupport;
private final GetterInterceptor getterInterceptor = new GetterInterceptor();
private final SetterInterceptor setterInterceptor = new SetterInterceptor();
private final AddListenerInterceptor addListenerInterceptor = new AddListenerInterceptor();
private final RemoveListenerInterceptor removeListenerInterceptor = new RemoveListenerInterceptor();
private final DelegateInterceptor delegateInterceptor = new DelegateInterceptor();
private final boolean multicast;
// -------------------------- STATIC METHODS --------------------------
/**
* Wrap the pojo using with no multicast .
*
* @param pojo plain old java object
* @return java bean
* @see #wrap(Object, boolean)
*/
public static Object wrap(Object pojo) {
return wrap(pojo, false);
}
/**
* Wrap the pojo. The pojo will be enhanced so that all setters will fire property change support. This method will try
* its best to synchronize the generated java bean's contents with those of the pojo. If that fails, it will log a
* warning.
*
* @param pojo plain old java object
* @param multicast whether the property change event will be multicast or not
* @return JavaBean
* @throws IllegalArgumentException if the pojo have been wrapped already
*/
public static Object wrap(Object pojo, boolean multicast) {
if (Enhancer.isEnhanced(pojo.getClass()) && pojo.getClass().getName().endsWith("JavaBeanAdapter")) {
throw new IllegalArgumentException("The pojo is already wrapped.");
}
Object bean;
IdentitySupport key = new IdentitySupport(pojo);
bean = cache.get(key);
if (bean == null) {
JavaBeanAdapter adapter = new JavaBeanAdapter(pojo, multicast);
Enhancer e = new Enhancer();
e.setSuperclass(pojo.getClass());
e.setNamingPolicy(namingPolicy);
e.setInterfaces(INTERFACES);
e.setCallbacks(
new Callback[]{
adapter.addListenerInterceptor,
adapter.removeListenerInterceptor,
adapter.getterInterceptor,
adapter.setterInterceptor,
adapter.delegateInterceptor
});
e.setCallbackFilter(INTERNAL_CALLBACK_FILTER);
bean = e.create();
try {
PropertyUtils.copyProperties(bean, pojo);
} catch (IllegalAccessException e1) {
log.log(Level.WARNING, "The caller does not have access to the property accessor method.", e1);
} catch (InvocationTargetException e1) {
log.log(Level.WARNING, "The property accessor method throws an exception.", e1);
} catch (NoSuchMethodException e1) {
log.log(Level.WARNING, "an accessor method for this propety cannot be found.", e1);
}
cache.put(key, bean);
}
return bean;
}
private static boolean isSetter(Method method) {
String name = method.getName();
return name.startsWith("set") &&
isVoidAndSingleArgument(method);
}
private static boolean isVoidAndSingleArgument(Method method) {
return method.getParameterTypes().length == 1 && method.getReturnType() == Void.TYPE;
}
private static boolean isNonJavaGetter(Method method) {
String name = method.getName();
boolean possibleGetter = name.startsWith("get") && method.getParameterTypes().length == 0;
if (possibleGetter) {
Class returnType = method.getReturnType();
return !(returnType.isPrimitive() || returnType.isArray() || returnType.getName().startsWith("java"));
}
return false;
}
private static String extractPropertyName(String name) {
char[] propName = name.substring(3).toCharArray();
propName[0] = Character.toLowerCase(propName[0]);
String propNameStr = String.valueOf(propName);
return propNameStr;
}
// --------------------------- CONSTRUCTORS ---------------------------
private JavaBeanAdapter(Object delegate, boolean multicast) {
assert delegate != null : "Delegate must be not null";
pojo = delegate;
propertySupport = new PropertyChangeSupport(delegate);
this.multicast = multicast;
}
// -------------------------- INNER CLASSES --------------------------
private static class IdentitySupport {
private Object object;
IdentitySupport(Object object) {
this.object = object;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof IdentitySupport)) return false;
final IdentitySupport identitySupport = (IdentitySupport) o;
return object == identitySupport.object;
}
public int hashCode() {
return System.identityHashCode(object);
}
}
private class GetterInterceptor
implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
Object retValFromSuper;
Object getterResult = methodProxy.invoke(pojo, objects);
if (getterResult != null && !Enhancer.isEnhanced(getterResult.getClass())) {
retValFromSuper = wrap(getterResult, false);
} else {
retValFromSuper = getterResult;
}
return retValFromSuper;
}
}
private class SetterInterceptor
implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
String name = method.getName();
String propNameStr = null;
Object oldValue = null;
Object newValue = null;
if (!multicast) {
propNameStr = extractPropertyName(name);
if (PropertyUtils.isReadable(pojo, propNameStr)) {
oldValue = PropertyUtils.getProperty(pojo, propNameStr);
}
newValue = objects[0];
}
methodProxy.invoke(pojo, objects);
// we try to minimize the symetric issue
methodProxy.invokeSuper(o, objects);
propertySupport.firePropertyChange(propNameStr, oldValue, newValue);
if (log.isLoggable(Level.FINE)) {
log.fine(name + " called");
}
return null;
}
}
private class AddListenerInterceptor
implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
propertySupport.addPropertyChangeListener((PropertyChangeListener) objects[0]);
return null;
}
}
private class RemoveListenerInterceptor
implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
propertySupport.removePropertyChangeListener((PropertyChangeListener) objects[0]);
return null;
}
}
private class DelegateInterceptor
implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws Throwable {
return methodProxy.invoke(pojo, objects);
}
}
private static class InternalCallbackFilter
implements CallbackFilter {
public int accept(Method method) {
int result;
String name = method.getName();
if ("addPropertyChangeListener".equals(name) && isVoidAndSingleArgument(method)) {
result = 0;
} else if ("removePropertyChangeListener".equals(name) && isVoidAndSingleArgument(method)) {
result = 1;
} else if (isNonJavaGetter(method)) {
result = 2;
} else if (isSetter(method)) {
result = 3;
} else {
result = 4;
}
if (log.isLoggable(Level.FINE)) {
log.fine(method + ":" + result);
}
return result;
}
}
private static class JBNamingPolicy
extends DefaultNamingPolicy {
public String getClassName(String prefix, String source, Object key, Predicate predicate) {
return super.getClassName(prefix, source, key, predicate) + "JavaBeanAdapter";
}
}
}
</code>
Here is the code for Observable:
<code>
package com.katalis.commons.util;
import java.beans.PropertyChangeListener;
/**
* Describes objects that provide bound properties as specified in the
* <a href="http://java.sun.com/products/javabeans/docs/spec.html">Java
* Bean Secification</a>.
*
* @author Karsten Lentzsch
*
* @see java.beans.PropertyChangeListener
* @see java.beans.PropertyChangeEvent
* @see java.beans.PropertyChangeSupport
*/
public interface Observable {
/**
* Adds a <code>PropertyChangeListener</code> to the listener list.
* The listener is registered for all bound properties of this class.
*
* @param listener the PropertyChangeListener to be added
*
* @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
*/
void addPropertyChangeListener(PropertyChangeListener listener);
/**
* Removes a <code>PropertyChangeListener</code> from the listener list.
* This method should be used to remove PropertyChangeListeners that were
* registered for all bound properties of this class.
*
* @param listener the PropertyChangeListener to be removed
*
* @see #addPropertyChangeListener(PropertyChangeListener)
*/
void removePropertyChangeListener(PropertyChangeListener listener);
}
</code>
You will need Commons-Beanutils from Apache Jakarta on the, I forgot the site. You can google them. I think you will need commons-logging as well. Don't forget to add CGLIB!
No comments:
Post a Comment