Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/ProxyManager.java =================================================================== --- . (revision 660113) +++ . (working copy) @@ -19,7 +19,10 @@ import javax.jcr.Node; import javax.jcr.Session; +import org.apache.jackrabbit.ocm.manager.beanconverter.BeanConverter; import org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter; +import org.apache.jackrabbit.ocm.mapper.model.BeanDescriptor; +import org.apache.jackrabbit.ocm.mapper.model.ClassDescriptor; import org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor; public interface ProxyManager { @@ -24,12 +27,10 @@ public interface ProxyManager { - public abstract Object createBeanProxy(Session session, - ObjectConverter objectConverter, Class beanClass, String path); + Object createBeanProxy(BeanConverter beanConverter, String path, Session session, Node parentNode, BeanDescriptor beanDescriptor, + ClassDescriptor beanClassDescriptor, Class beanClass, Object parent); - public abstract Object createCollectionProxy(Session session, - CollectionConverter collectionConverter, Node parentNode, - CollectionDescriptor collectionDescriptor, - Class collectionFieldClass); + Object createCollectionProxy(Session session, CollectionConverter collectionConverter, Node parentNode, + CollectionDescriptor collectionDescriptor, Class collectionFieldClass); } \ No newline at end of file Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/AbstractLazyLoader.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/AbstractLazyLoader.java (revision 0) +++ src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/AbstractLazyLoader.java (revision 0) @@ -0,0 +1,88 @@ +package org.apache.jackrabbit.ocm.manager.objectconverter.impl; + import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + import net.sf.cglib.proxy.InvocationHandler; + import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + /** + * Parent Class of the OCM Lazy Loaders + * + * @author Stephane LANDELLE + */ +public abstract class AbstractLazyLoader implements InvocationHandler { + /** + * The logger + */ + private final static Log log = LogFactory.getLog(AbstractLazyLoader.class); + /** + * The proxified instance + */ + private Object target = null; + /** + * Indicate if the proxy has been loaded + */ + private boolean initialized = false; + /** + * Return the proxified instance + * + * @return the proxified instance + */ + protected Object getTarget() { + if (!initialized) { + target = fetchTarget(); + initialized = true; + if (log.isDebugEnabled()) { + log.debug("Target loaded"); + } + } + return target; + } + /** + * Fetch the proxified instance + * + * @return the proxified instance + */ + protected abstract Object fetchTarget(); + /** + * Getter of property initialized + * + * @return initialized + */ + public boolean isInitialized() { + return initialized; + } + /** + * Invoke proxy methods : delegate to proxified instance except for OcmProxy + * methods that are intercepted (because not concretely implemented) + * + * @see net.sf.cglib.proxy.InvocationHandler#invoke(java.lang.Object, + * java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (!(proxy instanceof OcmProxy)) { + throw new IllegalArgumentException("proxy should implement OcmProxy"); + } + // proxified methods without concrete implementation + if (args.length == 0) { + if (method.getName().equals("isInitialized")) { + return isInitialized(); + } else if (method.getName().equals("fetch")) { + getTarget(); + return null; + } + } + Object returnValue = null; + if (Modifier.isPublic(method.getModifiers())) { + if (!method.getDeclaringClass().isInstance(getTarget())) { + throw new ClassCastException(getTarget().getClass().getName()); + } + returnValue = method.invoke(getTarget(), args); + } else { + if (!method.isAccessible()) { + method.setAccessible(true); + } + returnValue = method.invoke(getTarget(), args); + } + return returnValue == getTarget() ? proxy : returnValue; + } +} Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/BeanLazyLoader.java =================================================================== --- . (revision 660113) +++ . (working copy) @@ -17,35 +17,51 @@ package org.apache.jackrabbit.ocm.manager.objectconverter.impl; +import javax.jcr.Node; import javax.jcr.Session; -import net.sf.cglib.proxy.LazyLoader; +import org.apache.jackrabbit.ocm.manager.beanconverter.BeanConverter; +import org.apache.jackrabbit.ocm.mapper.model.BeanDescriptor; +import org.apache.jackrabbit.ocm.mapper.model.ClassDescriptor; + +public class BeanLazyLoader extends AbstractLazyLoader { + + private BeanConverter beanConverter; + private Session session; + private Node parentNode; + private BeanDescriptor beanDescriptor; + private ClassDescriptor beanClassDescriptor; + private Class beanClass; + private Object parent; + + public BeanLazyLoader(BeanConverter beanConverter, Session session, Node parentNode, BeanDescriptor beanDescriptor, + ClassDescriptor beanClassDescriptor, Class beanClass, Object parent) { + this.beanConverter = beanConverter; + this.session = session; + this.parentNode = parentNode; + this.beanDescriptor = beanDescriptor; + this.beanClassDescriptor = beanClassDescriptor; + this.beanClass = beanClass; + this.parent = parent; + } -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter; + @Override + protected Object fetchTarget() { + if (isInitialized()) { + throw new IllegalStateException("Proxy already initialized"); + } -public class BeanLazyLoader implements LazyLoader -{ + Object target = beanConverter.getObject(session, parentNode, beanDescriptor, beanClassDescriptor, beanClass, parent); - private final static Log log = LogFactory.getLog(BeanLazyLoader.class); - - private ObjectConverter objectConverter; - private Session session; - private Class beanClass; - private String path; - - - public BeanLazyLoader(ObjectConverter objectConverter, Session session, Class beanClass, String path) - { - this.objectConverter = objectConverter; - this.session = session; - this.beanClass = beanClass; - this.path = path; + clean(); + return target; } - public Object loadObject() - { - return objectConverter.getObject(session, beanClass, path); + private void clean() { + beanConverter = null; + session = null; + parentNode = null; + beanDescriptor = null; + parent = null; } } Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/CollectionLazyLoader.java =================================================================== --- . (revision 660113) +++ . (working copy) @@ -20,10 +20,6 @@ import javax.jcr.Node; import javax.jcr.Session; -import net.sf.cglib.proxy.LazyLoader; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter; import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjects; import org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor; @@ -28,9 +24,7 @@ import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjects; import org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor; -public class CollectionLazyLoader implements LazyLoader { - - private final static Log log = LogFactory.getLog(CollectionLazyLoader.class); +public class CollectionLazyLoader extends AbstractLazyLoader { private CollectionConverter collectionConverter; private Session session; @@ -36,10 +30,10 @@ private Session session; private Node collectionParentNode; private CollectionDescriptor collectionDescriptor; - private Class collectionFieldClass; + private Class collectionFieldClass; public CollectionLazyLoader(CollectionConverter collectionConverter, Session session, Node parentNode, - CollectionDescriptor collectionDescriptor, Class collectionFieldClass ) { + CollectionDescriptor collectionDescriptor, Class collectionFieldClass) { this.collectionConverter = collectionConverter; this.session = session; this.collectionParentNode = parentNode; @@ -47,10 +41,22 @@ this.collectionFieldClass = collectionFieldClass; } - public Object loadObject() { + @Override + protected Object fetchTarget() { + if (isInitialized()) { + throw new IllegalStateException("Proxy already initialized"); + } + ManageableObjects objects = collectionConverter.getCollection(session, collectionParentNode, collectionDescriptor, + collectionFieldClass); + Object target = objects.getObjects(); + clean(); + return target; + } - - ManageableObjects objects = collectionConverter.getCollection(session, collectionParentNode, collectionDescriptor, collectionFieldClass); - return objects.getObjects(); + private void clean() { + collectionConverter = null; + session = null; + collectionParentNode = null; + collectionDescriptor = null; } } Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/ObjectConverterImpl.java =================================================================== --- . (revision 660113) +++ . (working copy) @@ -34,6 +34,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.ocm.exception.IncorrectPersistentClassException; import org.apache.jackrabbit.ocm.exception.JcrMappingException; import org.apache.jackrabbit.ocm.exception.ObjectContentManagerException; import org.apache.jackrabbit.ocm.manager.ManagerConstant; @@ -42,9 +43,8 @@ import org.apache.jackrabbit.ocm.manager.cache.ObjectCache; import org.apache.jackrabbit.ocm.manager.cache.impl.RequestObjectCacheImpl; import org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter; -import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableCollection; +import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjects; import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjectsUtil; -import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjects; import org.apache.jackrabbit.ocm.manager.collectionconverter.impl.DefaultCollectionConverterImpl; import org.apache.jackrabbit.ocm.manager.collectionconverter.impl.ManageableCollectionImpl; import org.apache.jackrabbit.ocm.manager.collectionconverter.impl.ManageableMapImpl; @@ -656,7 +656,6 @@ { Class beanClass = ReflectionUtils.getPropertyType(object, beanName); - String converterClassName = null; if (null == beanDescriptor.getConverter() || "".equals(beanDescriptor.getConverter())) { @@ -671,7 +670,24 @@ BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName, param); if (beanDescriptor.isProxy()) { - bean = proxyManager.createBeanProxy(session, this, beanClass, beanConverter.getPath(session, beanDescriptor, node)); + if (beanDescriptor.getJcrType() != null && !"".equals(beanDescriptor.getJcrType())) { + // If a mapped jcrType has been set, use it as proxy parent class instead of the bean property type. + // This way, we can handle proxies when bean property type is an interface. + try { + String className = mapper.getClassDescriptorByNodeType(beanDescriptor.getJcrType()).getClassName(); + if (log.isDebugEnabled()) { + log.debug("a mapped jcrType has been specified, switching from <" + beanClass + "> to <" + ReflectionUtils.forName(className)); + } + beanClass = ReflectionUtils.forName(className); + + } catch (IncorrectPersistentClassException e) { + if (log.isDebugEnabled()) { + log.debug(beanDescriptor.getClassDescriptor().getJcrType() + " is not mapped"); + } + } + } + + bean = proxyManager.createBeanProxy(beanConverter, beanConverter.getPath(session, beanDescriptor, node), session, node, beanDescriptor, mapper.getClassDescriptorByClass(beanClass), beanClass, bean); } else { Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxy.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxy.java (revision 0) +++ src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxy.java (revision 0) @@ -0,0 +1,19 @@ +package org.apache.jackrabbit.ocm.manager.objectconverter.impl; + import java.io.Serializable; + /** + * Interface implemented by lazy loading proxies + * + * @author Stephane LANDELLE + */ +public interface OcmProxy extends Serializable { + /** + * Check is the proxy has been loaded + * + * @return true is the proxy has been loaded + */ + boolean isInitialized(); + /** + * Force proxy fetching + */ + void fetch(); +} Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxyUtils.java =================================================================== --- src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxyUtils.java (revision 0) +++ src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/OcmProxyUtils.java (revision 0) @@ -0,0 +1,47 @@ +package org.apache.jackrabbit.ocm.manager.objectconverter.impl; + /** + * Utils class for proxy nandling + * + * @author Stephane LANDELLE + */ +public abstract class OcmProxyUtils { + /** + * Check if an object is an OCM proxy + * + * @param object + * the Object to check + * @return true is the object is an OCM proxy + */ + public static boolean isProxy(Object object) { + return object instanceof OcmProxy; + } + /** + * Check is an Object is not an unitialized OCM proxy + * @see OcmProxy.isInitialized() + * + * @param object + * the Object to check + * @return true if the object is not an OCM proxy or if it has already been + * initialized + */ + public static boolean isInitialized(Object object) { + if (!isProxy(object)) { + return true; + } else { + return ((OcmProxy) object).isInitialized(); + } + } + /** + * Force fetching of an abject + * + * @param the type of the object to fetch + * @param object the object to fetch + * @return the fetched object + */ + public static T fetch(T object) { + if (isProxy(object)) { + ((OcmProxy) object).fetch(); + } + return object; + } +} Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/impl/ProxyManagerImpl.java =================================================================== --- . (revision 660113) +++ . (working copy) @@ -21,35 +21,28 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; +import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; -import net.sf.cglib.proxy.LazyLoader; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.lang.ArrayUtils; +import org.apache.jackrabbit.ocm.manager.beanconverter.BeanConverter; import org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter; -import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjectsUtil; -import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjects; -import org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter; import org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager; +import org.apache.jackrabbit.ocm.mapper.model.BeanDescriptor; +import org.apache.jackrabbit.ocm.mapper.model.ClassDescriptor; import org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor; -import org.apache.jackrabbit.ocm.reflection.ReflectionUtils; - -public class ProxyManagerImpl implements ProxyManager -{ - - private final static Log log = LogFactory.getLog(ProxyManagerImpl.class); - +public class ProxyManagerImpl implements ProxyManager { /** - * - * @see org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager#createBeanProxy(javax.jcr.Session, org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter, java.lang.Class, java.lang.String) + * @see org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager#createBeanProxy(javax.jcr.Session, + * org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter, + * java.lang.Class, java.lang.String) */ - public Object createBeanProxy(Session session, ObjectConverter objectConverter, Class beanClass, String path) - { - - try { - if (!session.itemExists(path)) { + public Object createBeanProxy(BeanConverter beanConverter, String path, Session session, Node parentNode, + BeanDescriptor beanDescriptor, ClassDescriptor beanClassDescriptor, Class beanClass, Object parent) { + try { + if (path == null || !session.itemExists(path)) { return null; } } catch (RepositoryException e) { @@ -56,24 +49,38 @@ throw new org.apache.jackrabbit.ocm.exception.RepositoryException("Impossible to check,if the object exits on " + path, e); } - LazyLoader loader = new BeanLazyLoader(objectConverter, session, beanClass, path) ; - return Enhancer.create(beanClass, loader); + Callback loader = new BeanLazyLoader(beanConverter, session, parentNode, beanDescriptor, beanClassDescriptor, beanClass, parent); + return Enhancer.create(beanClass, getInterfaces(beanClass), loader); } /** - * - * @see org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager#createCollectionProxy(javax.jcr.Session, org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter, javax.jcr.Node, org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor, java.lang.Class) + * @see org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager#createCollectionProxy(javax.jcr.Session, + * org.apache.jackrabbit.ocm.manager.collectionconverter.CollectionConverter, + * javax.jcr.Node, + * org.apache.jackrabbit.ocm.mapper.model.CollectionDescriptor, + * java.lang.Class) */ - public Object createCollectionProxy(Session session, CollectionConverter collectionConverter, Node parentNode, CollectionDescriptor collectionDescriptor, Class collectionFieldClass) - { + public Object createCollectionProxy(Session session, CollectionConverter collectionConverter, Node parentNode, + CollectionDescriptor collectionDescriptor, Class collectionFieldClass) { - if (collectionConverter.isNull(session, parentNode, collectionDescriptor, collectionFieldClass)) { + if (collectionConverter.isNull(session, parentNode, collectionDescriptor, collectionFieldClass)) { return null; } - //ManageableObjects manageableCollection = ManageableObjectsUtil.getManageableObjects(collectionFieldClass); - - LazyLoader loader = new CollectionLazyLoader(collectionConverter, session, parentNode, collectionDescriptor, collectionFieldClass); - return Enhancer.create(collectionFieldClass, loader); + Callback loader = new CollectionLazyLoader(collectionConverter, session, parentNode, collectionDescriptor, collectionFieldClass); + return Enhancer.create(collectionFieldClass, getInterfaces(collectionFieldClass), loader); + } + + private Class[] getInterfaces(Class collectionFieldClass) { + + Class[] interfaces = null; + if (collectionFieldClass.isInterface()) { + // if collectionFieldClass is an interface, simply use it + interfaces = new Class[] { collectionFieldClass }; + } else { + // else, use all interfaces + interfaces = collectionFieldClass.getInterfaces(); + } + return (Class[]) ArrayUtils.add(interfaces, OcmProxy.class); } }