org.apache.jackrabbit.ocm.manager.objectconverter.impl.ObjectConverterImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.jackrabbit.ocm.manager.objectconverter.impl.ObjectConverterImpl.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jackrabbit.ocm.manager.objectconverter.impl;

import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;

import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.jcr.version.VersionException;

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;
import org.apache.jackrabbit.ocm.manager.atomictypeconverter.AtomicTypeConverterProvider;
import org.apache.jackrabbit.ocm.manager.beanconverter.BeanConverter;
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.ManageableObjects;
import org.apache.jackrabbit.ocm.manager.collectionconverter.ManageableObjectsUtil;
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;
import org.apache.jackrabbit.ocm.manager.impl.ObjectContentManagerUtil;
import org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter;
import org.apache.jackrabbit.ocm.manager.objectconverter.ProxyManager;
import org.apache.jackrabbit.ocm.mapper.Mapper;
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.mapper.model.FieldDescriptor;
import org.apache.jackrabbit.ocm.reflection.ReflectionUtils;
import org.apache.jackrabbit.ocm.repository.NodeUtil;

/**
 * Default implementation for {@link ObjectConverterImpl}
 *
 * @author <a href="mailto:christophe.lombart@gmail.com">Lombart  Christophe </a>
 * @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
 */
public class ObjectConverterImpl implements ObjectConverter {

    private static final String DEFAULT_BEAN_CONVERTER = "org.apache.jackrabbit.ocm.manager.beanconverter.impl.DefaultBeanConverterImpl";

    private final static Log log = LogFactory.getLog(ObjectConverterImpl.class);

    private Mapper mapper;

    private AtomicTypeConverterProvider atomicTypeConverterProvider;

    private ProxyManager proxyManager;

    private SimpleFieldsHelper simpleFieldsHelp;

    private ObjectCache requestObjectCache;

    /**
     * No-arg constructor.
     */
    public ObjectConverterImpl() {
    }

    /**
     * Constructor
     *
     * @param mapper
     *            The mapper to used
     * @param converterProvider
     *            The atomic type converter provider
     *
     */
    public ObjectConverterImpl(Mapper mapper, AtomicTypeConverterProvider converterProvider) {
        this.mapper = mapper;
        this.atomicTypeConverterProvider = converterProvider;
        this.proxyManager = new ProxyManagerImpl();
        this.simpleFieldsHelp = new SimpleFieldsHelper(atomicTypeConverterProvider);
        this.requestObjectCache = new RequestObjectCacheImpl();
    }

    /**
     * Constructor
     *
     * @param mapper
     *            The mapper to used
     * @param converterProvider
     *            The atomic type converter provider
     *
     */
    public ObjectConverterImpl(Mapper mapper, AtomicTypeConverterProvider converterProvider,
            ProxyManager proxyManager, ObjectCache requestObjectCache) {
        this.mapper = mapper;
        this.atomicTypeConverterProvider = converterProvider;
        this.proxyManager = proxyManager;
        this.simpleFieldsHelp = new SimpleFieldsHelper(atomicTypeConverterProvider);
        this.requestObjectCache = requestObjectCache;
    }

    /**
     * Set the <code>Mapper</code> used to solve mappings.
     *
     * @param mapper
     *            a <code>Mapper</code>
     */
    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }

    /**
     * Sets the converter provider.
     *
     * @param converterProvider
     *            an <code>AtomicTypeConverterProvider</code>
     */
    public void setAtomicTypeConverterProvider(AtomicTypeConverterProvider converterProvider) {
        this.atomicTypeConverterProvider = converterProvider;
    }

    /**
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#insert(javax.jcr.Session,
     *      java.lang.Object)
     */
    public void insert(Session session, Object object) {
        String path = this.getPath(session, object);
        try {
            String parentPath = NodeUtil.getParentPath(path);
            String nodeName = NodeUtil.getNodeName(path);
            Node parentNode = (Node) session.getItem(parentPath);
            this.insert(session, parentNode, nodeName, object);

        } catch (PathNotFoundException pnfe) {
            throw new ObjectContentManagerException("Impossible to insert the object at '" + path + "'", pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to insert the object at '" + path + "'", re);
        }
    }

    /**
     *
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#insert(javax.jcr.Session,
     *      javax.jcr.Node, java.lang.String, java.lang.Object)
     */
    public void insert(Session session, Node parentNode, String nodeName, Object object) {
        ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(object.getClass());

        String jcrType = classDescriptor.getJcrType();
        if ((jcrType == null) || jcrType.equals("")) {
            jcrType = ManagerConstant.NT_UNSTRUCTURED;
        }

        Node objectNode = null;
        try {
            objectNode = parentNode.addNode(nodeName, jcrType);

        } catch (NoSuchNodeTypeException nsnte) {
            throw new JcrMappingException("Unknown node type " + jcrType + " for mapped class " + object.getClass(),
                    nsnte);
        } catch (RepositoryException re) {
            throw new ObjectContentManagerException(
                    "Cannot create new node of type " + jcrType + " from mapped class " + object.getClass(), re);
        }

        String[] mixinTypes = classDescriptor.getJcrMixinTypes();
        String mixinTypeName = null;
        try {

            // Add mixin types
            if (null != classDescriptor.getJcrMixinTypes()) {
                for (int i = 0; i < mixinTypes.length; i++) {
                    mixinTypeName = mixinTypes[i].trim();
                    objectNode.addMixin(mixinTypeName);
                }
            }

            // Add mixin types defined in the associated interfaces
            if (!classDescriptor.hasDiscriminator() && classDescriptor.hasInterfaces()) {
                Iterator interfacesIterator = classDescriptor.getImplements().iterator();
                while (interfacesIterator.hasNext()) {
                    String interfaceName = (String) interfacesIterator.next();
                    ClassDescriptor interfaceDescriptor = mapper
                            .getClassDescriptorByClass(ReflectionUtils.forName(interfaceName));
                    objectNode.addMixin(interfaceDescriptor.getJcrType().trim());
                }
            }

            // If required, add the discriminator node type
            if (classDescriptor.hasDiscriminator()) {
                addDiscriminatorProperty(object, objectNode);
            }

        } catch (NoSuchNodeTypeException nsnte) {
            throw new JcrMappingException(
                    "Unknown mixin type " + mixinTypeName + " for mapped class " + object.getClass(), nsnte);
        } catch (RepositoryException re) {
            throw new ObjectContentManagerException(
                    "Cannot create new node of type " + jcrType + " from mapped class " + object.getClass(), re);
        }

        simpleFieldsHelp.storeSimpleFields(session, object, classDescriptor, objectNode);
        insertBeanFields(session, object, classDescriptor, objectNode);
        insertCollectionFields(session, object, classDescriptor, objectNode);
        simpleFieldsHelp.refreshUuidPath(session, classDescriptor, objectNode, object);
    }

    private void addDiscriminatorProperty(Object object, Node objectNode)
            throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException,
            RepositoryException, ValueFormatException {

        try {
            objectNode.setProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY,
                    ReflectionUtils.getBeanClass(object).getName());

        } catch (Exception e) {
            // if it is not possible to add the CLASS_NAME_PROPERTY due to strong constraints in the
            // node type definition, try to add the Discriminator node type.
            String mixinTypeName;
            mixinTypeName = ManagerConstant.DISCRIMINATOR_NODE_TYPE;
            objectNode.addMixin(mixinTypeName);
            objectNode.setProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY,
                    ReflectionUtils.getBeanClass(object).getName());
        }

    }

    /**
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#update(javax.jcr.Session,
     *      java.lang.Object)
     */
    public void update(Session session, Object object) {
        String path = this.getPath(session, object);
        try {
            String parentPath = NodeUtil.getParentPath(path);
            String nodeName = NodeUtil.getNodeName(path);
            Node parentNode = (Node) session.getItem(parentPath);
            this.update(session, parentNode, nodeName, object);
        } catch (PathNotFoundException pnfe) {
            throw new ObjectContentManagerException("Impossible to update the object at '" + path + "'", pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to update the object at '" + path + "'", re);
        }
    }

    /**
     *
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#update(javax.jcr.Session,
     *      javax.jcr.Node, java.lang.String, java.lang.Object)
     */
    public void update(Session session, String uuId, Object object) {
        try {
            ClassDescriptor classDescriptor = mapper
                    .getClassDescriptorByClass(ReflectionUtils.getBeanClass(object));
            Node objectNode = session.getNodeByUUID(uuId);

            checkNodeType(session, classDescriptor);

            checkCompatiblePrimaryNodeTypes(session, objectNode, classDescriptor, false);

            simpleFieldsHelp.storeSimpleFields(session, object, classDescriptor, objectNode);
            updateBeanFields(session, object, classDescriptor, objectNode);
            updateCollectionFields(session, object, classDescriptor, objectNode);
            simpleFieldsHelp.refreshUuidPath(session, classDescriptor, objectNode, object);
        } catch (PathNotFoundException pnfe) {
            throw new ObjectContentManagerException("Impossible to update the object with UUID: " + uuId, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to update the object with UUID: " + uuId, re);
        }
    }

    /**
     *
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#update(javax.jcr.Session,
     *      javax.jcr.Node, java.lang.String, java.lang.Object)
     */
    public void update(Session session, Node parentNode, String nodeName, Object object) {
        try {
            ClassDescriptor classDescriptor = mapper
                    .getClassDescriptorByClass(ReflectionUtils.getBeanClass(object));
            Node objectNode = parentNode.getNode(nodeName);

            checkNodeType(session, classDescriptor);

            checkCompatiblePrimaryNodeTypes(session, objectNode, classDescriptor, false);

            simpleFieldsHelp.storeSimpleFields(session, object, classDescriptor, objectNode);
            updateBeanFields(session, object, classDescriptor, objectNode);
            updateCollectionFields(session, object, classDescriptor, objectNode);
            simpleFieldsHelp.refreshUuidPath(session, classDescriptor, objectNode, object);
        } catch (PathNotFoundException pnfe) {
            throw new ObjectContentManagerException(
                    "Impossible to update the object: " + nodeName + " at node : " + parentNode, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to update the object: " + nodeName + " at node : " + parentNode, re);
        }
    }

    /**
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#getObject(javax.jcr.Session,
     *      java.lang.Class, java.lang.String)
     */
    public Object getObject(Session session, String path) {
        try {
            if (!session.itemExists(path)) {
                return null;
            }

            if (requestObjectCache.isCached(path)) {
                return requestObjectCache.getObject(path);
            }

            ClassDescriptor classDescriptor = null;
            Node node = (Node) session.getItem(path);
            if (node.hasProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY)) {
                String className = node.getProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY).getValue()
                        .getString();
                classDescriptor = mapper.getClassDescriptorByClass(ReflectionUtils.forName(className));
            } else {
                String nodeType = node.getPrimaryNodeType().getName();
                if (nodeType.equals(ManagerConstant.FROZEN_NODE_TYPE)) {
                    nodeType = node.getProperty(ManagerConstant.FROZEN_PRIMARY_TYPE_PROPERTY).getString();
                }
                classDescriptor = mapper.getClassDescriptorByNodeType(nodeType);
            }

            if (null == classDescriptor) {
                throw new JcrMappingException("Impossible to find the classdescriptor for " + path
                        + ". There is no discriminator and associated  JCR node type");
            }

            Object object = ReflectionUtils.newInstance(classDescriptor.getClassName());

            if (!requestObjectCache.isCached(path)) {
                requestObjectCache.cache(path, object);
            }

            simpleFieldsHelp.retrieveSimpleFields(session, classDescriptor, node, object);
            retrieveBeanFields(session, classDescriptor, node, path, object, false);
            retrieveCollectionFields(session, classDescriptor, node, object, false);

            return object;

        } catch (PathNotFoundException pnfe) {
            // HINT should never get here
            throw new ObjectContentManagerException("Impossible to get the object at " + path, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to get the object at " + path, re);
        }
    }

    /**
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#getObject(javax.jcr.Session,
     *      java.lang.Class, java.lang.String)
     */
    public Object getObject(Session session, Class clazz, String path) {
        try {
            if (!session.itemExists(path)) {
                return null;
            }

            if (requestObjectCache.isCached(path)) {
                return requestObjectCache.getObject(path);
            }

            ClassDescriptor classDescriptor = getClassDescriptor(clazz);

            checkNodeType(session, classDescriptor);

            Node node = (Node) session.getItem(path);
            if (!classDescriptor.isInterface()) {
                node = getActualNode(session, node);
                checkCompatiblePrimaryNodeTypes(session, node, classDescriptor, true);
            }

            ClassDescriptor alternativeDescriptor = null;
            if (classDescriptor.usesNodeTypePerHierarchyStrategy()) {
                if (node.hasProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY)) {
                    String className = node.getProperty(ManagerConstant.DISCRIMINATOR_CLASS_NAME_PROPERTY)
                            .getValue().getString();
                    alternativeDescriptor = getClassDescriptor(ReflectionUtils.forName(className));
                }
            } else {
                if (classDescriptor.usesNodeTypePerConcreteClassStrategy()) {
                    String nodeType = node.getPrimaryNodeType().getName();
                    if (!nodeType.equals(classDescriptor.getJcrType())) {
                        alternativeDescriptor = classDescriptor.getDescendantClassDescriptor(nodeType);

                        // in case we an alternative could not be found by walking
                        // the class descriptor hierarchy, check whether we would
                        // have a descriptor for the node type directly (which
                        // may the case if the class descriptor hierarchy is
                        // incomplete due to missing configuration. See JCR-1145
                        // for details.
                        if (alternativeDescriptor == null) {
                            alternativeDescriptor = mapper.getClassDescriptorByNodeType(nodeType);
                        }
                    }
                }
            }

            // if we have an alternative class descriptor, check whether its
            // extends (or is the same) as the requested class.
            if (alternativeDescriptor != null) {
                Class alternativeClazz = ReflectionUtils.forName(alternativeDescriptor.getClassName());
                if (clazz.isAssignableFrom(alternativeClazz)) {
                    clazz = alternativeClazz;
                    classDescriptor = alternativeDescriptor;
                }
            }

            // ensure class is concrete (neither interface nor abstract)
            if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) {
                throw new JcrMappingException("Cannot instantiate non-concrete class " + clazz.getName()
                        + " for node " + path + " of type " + node.getPrimaryNodeType().getName());
            }

            Object object = ReflectionUtils.newInstance(classDescriptor.getClassName());

            if (!requestObjectCache.isCached(path)) {
                requestObjectCache.cache(path, object);
            }

            simpleFieldsHelp.retrieveSimpleFields(session, classDescriptor, node, object);
            retrieveBeanFields(session, classDescriptor, node, path, object, false);
            retrieveCollectionFields(session, classDescriptor, node, object, false);

            return object;
        } catch (PathNotFoundException pnfe) {
            // HINT should never get here
            throw new ObjectContentManagerException("Impossible to get the object at " + path, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to get the object at " + path, re);
        }
    }

    public void retrieveAllMappedAttributes(Session session, Object object) {
        String path = null;
        try {
            ClassDescriptor classDescriptor = getClassDescriptor(object.getClass());
            String pathFieldName = classDescriptor.getPathFieldDescriptor().getFieldName();
            path = (String) ReflectionUtils.getNestedProperty(object, pathFieldName);
            Node node = (Node) session.getItem(path);
            retrieveBeanFields(session, classDescriptor, node, path, object, true);
            retrieveCollectionFields(session, classDescriptor, node, object, true);

        } catch (PathNotFoundException pnfe) {

            throw new ObjectContentManagerException("Impossible to get the object at " + path, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to get the object at " + path, re);
        }
    }

    public void retrieveMappedAttribute(Session session, Object object, String attributeName) {
        String path = null;
        ClassDescriptor classDescriptor = null;
        try {
            classDescriptor = getClassDescriptor(object.getClass());
            String pathFieldName = classDescriptor.getPathFieldDescriptor().getFieldName();
            path = (String) ReflectionUtils.getNestedProperty(object, pathFieldName);
            Node node = (Node) session.getItem(path);
            BeanDescriptor beanDescriptor = classDescriptor.getBeanDescriptor(attributeName);
            if (beanDescriptor != null) {
                this.retrieveBeanField(session, beanDescriptor, node, path, object, true);
            }
            // Check if the attribute is a collection
            else {
                CollectionDescriptor collectionDescriptor = classDescriptor.getCollectionDescriptor(attributeName);
                if (collectionDescriptor != null) {
                    this.retrieveCollectionField(session, collectionDescriptor, node, object, true);
                } else {
                    throw new ObjectContentManagerException(
                            "Impossible to retrieve the mapped attribute. The attribute '" + attributeName
                                    + "'  is not a bean or a collection for the class : "
                                    + classDescriptor.getClassName());
                }
            }

        } catch (PathNotFoundException pnfe) {

            throw new ObjectContentManagerException("Impossible to get the object at " + path, pnfe);
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(
                    "Impossible to get the object at " + path, re);
        }
    }

    /**
     * Validates the node type used by the class descriptor.
     *
     * @param session
     *            the current session
     * @param classDescriptor
     *            descriptor
     * @throws JcrMappingException
     *             thrown if the node type is unknown
     * @throws org.apache.jackrabbit.ocm.exception.RepositoryException
     *             thrown if an error occured in the underlying repository
     */
    private void checkNodeType(Session session, ClassDescriptor classDescriptor) {
        String jcrTypeName = null;
        try {

            //Don't check the primary node type for interfaces. They are only associated to mixin node type
            if (classDescriptor.isInterface()) {
                String[] mixinTypes = classDescriptor.getJcrMixinTypes();
                for (int i = 0; i < mixinTypes.length; i++) {
                    jcrTypeName = mixinTypes[i];
                    session.getWorkspace().getNodeTypeManager().getNodeType(jcrTypeName);
                }
            } else {
                jcrTypeName = classDescriptor.getJcrType();
                if (jcrTypeName != null && !jcrTypeName.equals("")) {
                    session.getWorkspace().getNodeTypeManager().getNodeType(jcrTypeName);
                }
            }
        } catch (NoSuchNodeTypeException nsnte) {
            throw new JcrMappingException("Mapping for class '" + classDescriptor.getClassName()
                    + "' use unknown primary or mixin node type '" + jcrTypeName + "'");
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(re);
        }
    }

    /**
     * Checks if the node type in the class descriptor is compatible with the
     * specified node node type.
     *
     * @param session
     *            the current session
     * @param node
     *            node against whose node type the compatibility is checked
     * @param classDescriptor
     *            class descriptor
     * @param checkVersionNode
     *            <tt>true</tt> if the check should continue in case the
     *            <tt>node</tt> is a version node, <tt>false</tt> if no
     *            check against version node should be performed
     *
     * @throws ObjectContentManagerException
     *             thrown if node types are incompatible
     * @throws org.apache.jackrabbit.ocm.exception.RepositoryException
     *             thrown if an error occured in the underlying repository
     */
    private void checkCompatiblePrimaryNodeTypes(Session session, Node node, ClassDescriptor classDescriptor,
            boolean checkVersionNode) {
        try {
            NodeType nodeType = node.getPrimaryNodeType();

            boolean compatible = checkCompatibleNodeTypes(nodeType, classDescriptor);

            if (!compatible && checkVersionNode && ManagerConstant.FROZEN_NODE_TYPE.equals(nodeType.getName())) {
                NodeTypeManager ntMgr = session.getWorkspace().getNodeTypeManager();
                nodeType = ntMgr
                        .getNodeType(node.getProperty(ManagerConstant.FROZEN_PRIMARY_TYPE_PROPERTY).getString());

                compatible = checkCompatibleNodeTypes(nodeType, classDescriptor);
            }

            if (!compatible) {
                throw new ObjectContentManagerException("Cannot map object of type '"
                        + classDescriptor.getClassName() + "'. Node type '" + node.getPrimaryNodeType().getName()
                        + "' does not match descriptor node type '" + classDescriptor.getJcrType() + "'");
            }
        } catch (RepositoryException re) {
            throw new org.apache.jackrabbit.ocm.exception.RepositoryException(re);
        }
    }

    /**
     * Node types compatibility check.
     *
     * @param nodeType
     *            target node type
     * @param descriptor
     *            descriptor containing source node type
     * @return <tt>true</tt> if nodes are considered compatible,
     *         <tt>false</tt> otherwise
     */
    private boolean checkCompatibleNodeTypes(NodeType nodeType, ClassDescriptor descriptor) {

        //return true if node type is not used
        if (descriptor.getJcrType() == null || descriptor.getJcrType().equals("")) {
            return true;
        }

        if (nodeType.getName().equals(descriptor.getJcrType())) {
            return true;
        }

        NodeType[] superTypes = nodeType.getSupertypes();
        for (int i = 0; i < superTypes.length; i++) {
            if (superTypes[i].getName().equals(descriptor.getJcrType())) {
                return true;
            }
        }

        return false;
    }

    /**
     * @see org.apache.jackrabbit.ocm.manager.objectconverter.ObjectConverter#getPath(javax.jcr.Session,
     *      java.lang.Object)
     * @throws JcrMappingException
     */
    public String getPath(Session session, Object object) {
        ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(object.getClass());

        final FieldDescriptor pathFieldDescriptor = classDescriptor.getPathFieldDescriptor();
        if (pathFieldDescriptor == null) {
            throw new JcrMappingException("Class of type: " + object.getClass().getName()
                    + " has no path mapping. Maybe attribute path=\"true\" for a field element of this class in mapping descriptor is missing "
                    + " or maybe it is defined in an ancestor class which has no mapping descriptor.");
        }
        String pathField = pathFieldDescriptor.getFieldName();

        return (String) ReflectionUtils.getNestedProperty(object, pathField);
    }

    /**
     * Retrieve bean fields
     */
    private void retrieveBeanFields(Session session, ClassDescriptor classDescriptor, Node node, String path,
            Object object, boolean forceToRetrieve) {
        Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
        while (beanDescriptorIterator.hasNext()) {
            BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
            this.retrieveBeanField(session, beanDescriptor, node, path, object, forceToRetrieve);
        }
    }

    private void retrieveBeanField(Session session, BeanDescriptor beanDescriptor, Node node, String path,
            Object object, boolean forceToRetrieve) {
        if (!beanDescriptor.isAutoRetrieve() && !forceToRetrieve) {
            return;
        }

        String beanName = beanDescriptor.getFieldName();
        String beanPath = ObjectContentManagerUtil.getPath(session, beanDescriptor, node);

        Object bean = null;
        if (requestObjectCache.isCached(beanPath)) {
            bean = requestObjectCache.getObject(beanPath);
            ReflectionUtils.setNestedProperty(object, beanName, bean);
        } else {
            Class beanClass = ReflectionUtils.getPropertyType(object, beanName);

            String converterClassName = null;
            if (null == beanDescriptor.getConverter() || "".equals(beanDescriptor.getConverter())) {
                converterClassName = DEFAULT_BEAN_CONVERTER;
            } else {
                converterClassName = beanDescriptor.getConverter();
            }

            Object[] param = { this.mapper, this, this.atomicTypeConverterProvider };
            BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName,
                    param);
            if (beanDescriptor.isProxy()) {
                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 {
                bean = beanConverter.getObject(session, node, beanDescriptor,
                        mapper.getClassDescriptorByClass(beanClass), beanClass, bean);
            }
            requestObjectCache.cache(beanPath, bean);
            ReflectionUtils.setNestedProperty(object, beanName, bean);
        }
    }

    /**
     * Retrieve Collection fields
     */
    private void retrieveCollectionFields(Session session, ClassDescriptor classDescriptor, Node parentNode,
            Object object, boolean forceToRetrieve) {
        Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();
        while (collectionDescriptorIterator.hasNext()) {
            CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();
            this.retrieveCollectionField(session, collectionDescriptor, parentNode, object, forceToRetrieve);
        }
    }

    private void retrieveCollectionField(Session session, CollectionDescriptor collectionDescriptor,
            Node parentNode, Object object, boolean forceToRetrieve) {
        if (!collectionDescriptor.isAutoRetrieve() && !forceToRetrieve) {
            return;
        }

        CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
        Class collectionFieldClass = ReflectionUtils.getPropertyType(object, collectionDescriptor.getFieldName());
        ManageableObjects objects = null;
        if (collectionDescriptor.isProxy()) {
            Object proxy = proxyManager.createCollectionProxy(session, collectionConverter, parentNode,
                    collectionDescriptor, collectionFieldClass);
            ReflectionUtils.setNestedProperty(object, collectionDescriptor.getFieldName(), proxy);
        } else {
            objects = collectionConverter.getCollection(session, parentNode, collectionDescriptor,
                    collectionFieldClass);
            if (objects == null) {
                ReflectionUtils.setNestedProperty(object, collectionDescriptor.getFieldName(), null);
            } else {
                // TODO: find another for managing custom ManageableObjects classes
                if (!objects.getClass().equals(ManageableCollectionImpl.class)
                        && !objects.getClass().equals(ManageableMapImpl.class)) {
                    ReflectionUtils.setNestedProperty(object, collectionDescriptor.getFieldName(), objects);
                } else {
                    ReflectionUtils.setNestedProperty(object, collectionDescriptor.getFieldName(),
                            objects.getObjects());
                }
            }

        }

    }

    /**
     * Insert Bean fields
     */
    private void insertBeanFields(Session session, Object object, ClassDescriptor classDescriptor,
            Node objectNode) {
        Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
        while (beanDescriptorIterator.hasNext()) {
            BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();

            if (!beanDescriptor.isAutoInsert()) {
                continue;
            }

            String jcrName = beanDescriptor.getJcrName();
            Object bean = ReflectionUtils.getNestedProperty(object, beanDescriptor.getFieldName());
            if (bean != null) {
                String converterClassName = null;

                if (null == beanDescriptor.getConverter() || "".equals(beanDescriptor.getConverter())) {
                    converterClassName = DEFAULT_BEAN_CONVERTER;
                } else {
                    converterClassName = beanDescriptor.getConverter();
                }

                Object[] param = { this.mapper, this, this.atomicTypeConverterProvider };
                BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName,
                        param);
                beanConverter.insert(session, objectNode, beanDescriptor,
                        mapper.getClassDescriptorByClass(bean.getClass()), bean, classDescriptor, object);
            }
        }
    }

    /**
     * Update Bean fields
     */
    private void updateBeanFields(Session session, Object object, ClassDescriptor classDescriptor,
            Node objectNode) {
        String jcrName = null;
        Iterator beanDescriptorIterator = classDescriptor.getBeanDescriptors().iterator();
        while (beanDescriptorIterator.hasNext()) {
            BeanDescriptor beanDescriptor = (BeanDescriptor) beanDescriptorIterator.next();
            if (!beanDescriptor.isAutoUpdate()) {
                continue;
            }

            jcrName = beanDescriptor.getJcrName();
            Object bean = ReflectionUtils.getNestedProperty(object, beanDescriptor.getFieldName());

            String converterClassName = null;
            if (null == beanDescriptor.getConverter() || "".equals(beanDescriptor.getConverter())) {
                converterClassName = DEFAULT_BEAN_CONVERTER;
            } else {
                converterClassName = beanDescriptor.getConverter();
            }

            Object[] param = { this.mapper, this, this.atomicTypeConverterProvider };
            BeanConverter beanConverter = (BeanConverter) ReflectionUtils.invokeConstructor(converterClassName,
                    param);
            Class beanClass = ReflectionUtils.getPropertyType(object, beanDescriptor.getFieldName());
            // if the bean is null, remove existing node
            if ((bean == null)) {

                beanConverter.remove(session, objectNode, beanDescriptor,
                        mapper.getClassDescriptorByClass(beanClass), bean, classDescriptor, object);

            } else {
                beanConverter.update(session, objectNode, beanDescriptor,
                        mapper.getClassDescriptorByClass(beanClass), bean, classDescriptor, object);
            }

        }
    }

    private void insertCollectionFields(Session session, Object object, ClassDescriptor classDescriptor,
            Node objectNode) {
        Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();

        while (collectionDescriptorIterator.hasNext()) {
            CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();

            if (!collectionDescriptor.isAutoInsert()) {
                continue;
            }

            CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
            Object collection = ReflectionUtils.getNestedProperty(object, collectionDescriptor.getFieldName());
            ManageableObjects manageableCollection = ManageableObjectsUtil.getManageableObjects(collection);

            collectionConverter.insertCollection(session, objectNode, collectionDescriptor, manageableCollection);
        }
    }

    private void updateCollectionFields(Session session, Object object, ClassDescriptor classDescriptor,
            Node objectNode) {
        Iterator collectionDescriptorIterator = classDescriptor.getCollectionDescriptors().iterator();

        while (collectionDescriptorIterator.hasNext()) {
            CollectionDescriptor collectionDescriptor = (CollectionDescriptor) collectionDescriptorIterator.next();
            if (!collectionDescriptor.isAutoUpdate()) {
                continue;
            }

            CollectionConverter collectionConverter = this.getCollectionConverter(session, collectionDescriptor);
            Object collection = ReflectionUtils.getNestedProperty(object, collectionDescriptor.getFieldName());
            ManageableObjects manageableCollection = ManageableObjectsUtil.getManageableObjects(collection);

            collectionConverter.updateCollection(session, objectNode, collectionDescriptor, manageableCollection);
        }
    }

    private CollectionConverter getCollectionConverter(Session session, CollectionDescriptor collectionDescriptor) {
        String className = collectionDescriptor.getCollectionConverter();
        Map atomicTypeConverters = this.atomicTypeConverterProvider.getAtomicTypeConverters();
        if (className == null) {
            return new DefaultCollectionConverterImpl(atomicTypeConverters, this, this.mapper);
        } else {
            return (CollectionConverter) ReflectionUtils.invokeConstructor(className,
                    new Object[] { atomicTypeConverters, this, this.mapper });
        }

    }

    private ClassDescriptor getClassDescriptor(Class beanClass) {
        ClassDescriptor classDescriptor = mapper.getClassDescriptorByClass(beanClass);
        if (null == classDescriptor) {
            throw new JcrMappingException("Class of type: " + beanClass.getName()
                    + " is not JCR persistable. Maybe element 'class-descriptor' for this type in mapping file is missing");
        }

        return classDescriptor;
    }

    private Node getActualNode(Session session, Node node) throws RepositoryException {
        NodeType type = node.getPrimaryNodeType();
        if (type.getName().equals("nt:versionedChild")) {

            String uuid = node.getProperty("jcr:childVersionHistory").getValue().getString();
            Node actualNode = session.getNodeByUUID(uuid);
            String name = actualNode.getName();
            actualNode = session.getNodeByUUID(name);

            return actualNode;
        }

        return node;
    }

}