org.jspresso.framework.model.persistence.mongo.JspressoEntityReadConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.jspresso.framework.model.persistence.mongo.JspressoEntityReadConverter.java

Source

/*
 * Copyright (c) 2005-2016 Vincent Vandenschrick. All rights reserved.
 *
 *  This file is part of the Jspresso framework.
 *
 *  Jspresso is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Jspresso is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with Jspresso.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jspresso.framework.model.persistence.mongo;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import com.mongodb.BasicDBList;
import com.mongodb.DBObject;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.data.mongodb.core.MongoTemplate;

import org.jspresso.framework.application.backend.BackendControllerHolder;
import org.jspresso.framework.application.backend.IBackendController;
import org.jspresso.framework.model.component.IComponent;
import org.jspresso.framework.model.component.IComponentCollectionFactory;
import org.jspresso.framework.model.descriptor.ICollectionPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IComponentDescriptor;
import org.jspresso.framework.model.descriptor.IPropertyDescriptor;
import org.jspresso.framework.model.descriptor.IReferencePropertyDescriptor;
import org.jspresso.framework.model.descriptor.IRelationshipEndPropertyDescriptor;
import org.jspresso.framework.model.entity.IEntity;
import org.jspresso.framework.model.entity.IEntityFactory;
import org.jspresso.framework.model.entity.IEntityRegistry;
import org.jspresso.framework.model.entity.basic.BasicEntityRegistry;
import org.jspresso.framework.util.bean.PropertyHelper;

/**
 * Custom converter for Jspresso entities.
 *
 * @author Vincent Vandenschrick
 */
public class JspressoEntityReadConverter
        implements ConditionalGenericConverter, ApplicationListener<ContextRefreshedEvent> {

    private IEntityFactory entityFactory;
    private IComponentCollectionFactory collectionFactory;
    private MongoTemplate mongo;
    private JspressoMappingMongoConverter converter;

    /**
     * Convert object.
     *
     * @param source
     *     the generic source
     * @param sourceType
     *     the source type
     * @param targetType
     *     the target type
     * @return the object
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
        IEntityRegistry readerRegistry = new BasicEntityRegistry("JspressoEntityReadConverter");
        return convertEntity((DBObject) source, (Class<? extends IEntity>) targetType.getType(), readerRegistry);
    }

    @SuppressWarnings("unchecked")
    private IEntity convertEntity(DBObject source, Class<? extends IEntity> entityType,
            IEntityRegistry readerRegistry) {
        Serializable id = (Serializable) source.get("_id");
        IComponentDescriptor<? extends IEntity> entityDescriptor = (IComponentDescriptor<? extends IEntity>) getEntityFactory()
                .getComponentDescriptor(entityType);
        IEntity entity = getBackendController().getUnitOfWorkOrRegisteredEntity(entityType, id);
        if (entity == null) {
            entity = readerRegistry.get(entityType, id);
            if (entity == null) {
                entity = getEntityFactory().createEntityInstance(entityType, id);
                readerRegistry.register(entityType, id, entity);
                completeComponent(source, entityDescriptor, entity, readerRegistry);
                if (entity.getVersion() == null) {
                    // Make sure that even if persistent store does not have a version property, the entity has one and is
                    // considered persistent.
                    entity.setVersion(0);
                }
            }
        }
        return entity;
    }

    @SuppressWarnings("unchecked")
    private IEntity convertEntity(Serializable id, Class<IEntity> entityType, IEntityRegistry readerRegistry) {
        IEntity entity = getBackendController().getUnitOfWorkOrRegisteredEntity(entityType, id);
        if (entity == null) {
            entity = readerRegistry.get(entityType, id);
            if (entity == null) {
                entity = createProxyEntity(id, entityType);
                readerRegistry.register(entityType, id, entity);
            }
        }
        return entity;
    }

    @SuppressWarnings("unchecked")
    private Object convertComponent(DBObject source, Class<? extends IComponent> componentType,
            IEntityRegistry readerRegistry) {
        IComponentDescriptor<? extends IComponent> componentDescriptor = (IComponentDescriptor<? extends IComponent>) getEntityFactory()
                .getComponentDescriptor(componentType);
        IComponent component = getEntityFactory().createComponentInstance(componentType);
        completeComponent(source, componentDescriptor, component, readerRegistry);
        return component;
    }

    @SuppressWarnings("unchecked")
    private void completeComponent(DBObject source, IComponentDescriptor<? extends IComponent> entityDescriptor,
            IComponent component, IEntityRegistry readerRegistry) {
        Class<? extends IComponent> componentContract = component.getComponentContract();
        for (IPropertyDescriptor propertyDescriptor : entityDescriptor.getPropertyDescriptors()) {
            if (propertyDescriptor != null && !propertyDescriptor.isComputed()) {
                String propertyName = propertyDescriptor.getName();
                Class<?> propertyType = propertyDescriptor.getModelType();
                String convertedPropertyName = getConverter().getMappingContext()
                        .getPersistentEntity(componentContract)
                        .getPersistentProperty(PropertyHelper.toJavaBeanPropertyName(propertyName)).getFieldName();
                if (source.containsField(convertedPropertyName)) {
                    Object propertyValue = source.get(convertedPropertyName);
                    Class<?> componentRefType = null;
                    if (propertyDescriptor instanceof IRelationshipEndPropertyDescriptor) {
                        if (propertyDescriptor instanceof IReferencePropertyDescriptor<?>) {
                            componentRefType = ((IReferencePropertyDescriptor<?>) propertyDescriptor)
                                    .getReferencedDescriptor().getModelType();
                        } else if (propertyDescriptor instanceof ICollectionPropertyDescriptor<?>) {
                            componentRefType = ((ICollectionPropertyDescriptor<?>) propertyDescriptor)
                                    .getCollectionDescriptor().getElementDescriptor().getModelType();
                        }
                    }
                    if (propertyValue instanceof DBObject) {
                        if (propertyValue instanceof BasicDBList) {
                            if (propertyDescriptor instanceof ICollectionPropertyDescriptor<?>) {
                                Class<? extends Collection<?>> collectionInterface = ((ICollectionPropertyDescriptor) propertyDescriptor)
                                        .getCollectionDescriptor().getCollectionInterface();
                                if (IComponent.class.isAssignableFrom(componentRefType)) {
                                    if (IEntity.class.isAssignableFrom(componentRefType)) {
                                        Collection<Serializable> collectionProperty = getCollectionFactory()
                                                .createComponentCollection(collectionInterface);
                                        for (Object element : (BasicDBList) propertyValue) {
                                            collectionProperty.add((Serializable) element);
                                        }
                                        component.straightSetProperty(propertyName,
                                                createProxyCollection(collectionProperty,
                                                        (Class<IEntity>) componentRefType, collectionInterface));
                                    } else {
                                        Collection<Object> collectionProperty = getCollectionFactory()
                                                .createComponentCollection(collectionInterface);
                                        for (Object element : (BasicDBList) propertyValue) {
                                            if (element instanceof DBObject) {
                                                collectionProperty.add(convertComponent((DBObject) element,
                                                        (Class<? extends IComponent>) componentRefType,
                                                        readerRegistry));
                                            }
                                        }
                                        component.straightSetProperty(propertyName, collectionProperty);
                                    }
                                } else {
                                    Collection<Object> collectionProperty = getCollectionFactory()
                                            .createComponentCollection(collectionInterface);
                                    for (Object element : (BasicDBList) propertyValue) {
                                        collectionProperty.add(element);
                                    }
                                    component.straightSetProperty(propertyName, collectionProperty);
                                }
                            } else {
                                component.straightSetProperty(propertyName, propertyValue);
                            }
                        } else if (propertyDescriptor instanceof IReferencePropertyDescriptor<?>) {
                            component.straightSetProperty(propertyName, convertComponent((DBObject) propertyValue,
                                    (Class<? extends IComponent>) componentRefType, readerRegistry));
                        } else {
                            Object convertedPropertyValue = getConverter().read(propertyType,
                                    (DBObject) propertyValue);
                            component.straightSetProperty(propertyName, convertedPropertyValue);
                        }
                    } else if (componentRefType != null && propertyValue instanceof Serializable) {
                        component.straightSetProperty(propertyName, convertEntity((Serializable) propertyValue,
                                (Class<IEntity>) componentRefType, readerRegistry));
                    } else {
                        Object convertedPropertyValue = getConverter().getConversionService().convert(propertyValue,
                                propertyType);
                        component.straightSetProperty(propertyName, convertedPropertyValue);
                    }
                }
            }
        }
    }

    /**
     * Gets entity factory.
     *
     * @return the entity factory
     */
    protected IEntityFactory getEntityFactory() {
        return entityFactory;
    }

    /**
     * Sets entity factory.
     *
     * @param entityFactory
     *     the entity factory
     */
    public void setEntityFactory(IEntityFactory entityFactory) {
        this.entityFactory = entityFactory;
    }

    /**
     * Matches boolean.
     *
     * @param sourceType
     *     the source type
     * @param targetType
     *     the target type
     * @return the boolean
     */
    @Override
    public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
        return DBObject.class.isAssignableFrom(sourceType.getType())
                && IEntity.class.isAssignableFrom(targetType.getType());
    }

    /**
     * Gets convertible types.
     *
     * @return the convertible types
     */
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(DBObject.class, IEntity.class));
    }

    /**
     * Gets collection factory.
     *
     * @return the collection factory
     */
    protected IComponentCollectionFactory getCollectionFactory() {
        return collectionFactory;
    }

    /**
     * Sets collection factory.
     *
     * @param collectionFactory
     *     the collection factory
     */
    public void setCollectionFactory(IComponentCollectionFactory collectionFactory) {
        this.collectionFactory = collectionFactory;
    }

    /**
     * Gets mongo.
     *
     * @return the mongo
     */
    protected MongoTemplate getMongo() {
        return mongo;
    }

    /**
     * Sets mongo.
     *
     * @param mongo
     *     the mongo
     */
    public void setMongo(MongoTemplate mongo) {
        this.mongo = mongo;
    }

    /**
     * On application event.
     *
     * @param event
     *     the event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        setMongo(event.getApplicationContext().getBean("mongoTemplate", MongoTemplate.class));
    }

    private IEntity createProxyEntity(Serializable id, Class<IEntity> entityContract) {
        return (IEntity) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[] { entityContract, JspressoMongoEntityProxy.class },
                new JspressoMongoEntityProxyHandler(id, entityContract, getMongo()));
    }

    private Object createProxyCollection(Collection<Serializable> ids, Class<IEntity> entityContract,
            Class<? extends Collection<?>> collectionContract) {
        InvocationHandler handler;
        if (List.class.isAssignableFrom(collectionContract)) {
            handler = new JspressoMongoEntityListHandler(ids, entityContract, getMongo());
        } else {
            handler = new JspressoMongoEntitySetHandler(ids, entityContract, getMongo());
        }
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[] { collectionContract, JspressoMongoProxy.class }, handler);
    }

    /**
     * Gets the getBackendController().
     *
     * @return the backendController.
     */
    protected IBackendController getBackendController() {
        return BackendControllerHolder.getCurrentBackendController();
    }

    /**
     * Gets converter.
     *
     * @return the converter
     */
    protected JspressoMappingMongoConverter getConverter() {
        return converter;
    }

    /**
     * Sets converter.
     *
     * @param converter
     *     the converter
     */
    public void setConverter(JspressoMappingMongoConverter converter) {
        this.converter = converter;
    }
}