org.bonitasoft.engine.business.data.impl.BusinessDataServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.bonitasoft.engine.business.data.impl.BusinessDataServiceImpl.java

Source

/**
 * Copyright (C) 2015 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library 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
 * version 2.1 of the License.
 * This library 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 this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.business.data.impl;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import org.apache.commons.lang3.StringUtils;
import org.bonitasoft.engine.bdm.BDMQueryUtil;
import org.bonitasoft.engine.bdm.Entity;
import org.bonitasoft.engine.bdm.model.BusinessObject;
import org.bonitasoft.engine.bdm.model.BusinessObjectModel;
import org.bonitasoft.engine.bdm.model.Query;
import org.bonitasoft.engine.bdm.model.QueryParameter;
import org.bonitasoft.engine.bdm.model.field.RelationField.Type;
import org.bonitasoft.engine.business.data.BusinessDataModelRepository;
import org.bonitasoft.engine.business.data.BusinessDataRepository;
import org.bonitasoft.engine.business.data.BusinessDataService;
import org.bonitasoft.engine.business.data.JsonBusinessDataSerializer;
import org.bonitasoft.engine.business.data.SBusinessDataNotFoundException;
import org.bonitasoft.engine.business.data.SBusinessDataRepositoryException;
import org.bonitasoft.engine.commons.ClassReflector;
import org.bonitasoft.engine.commons.JavaMethodInvoker;
import org.bonitasoft.engine.commons.TypeConverterUtil;
import org.bonitasoft.engine.commons.exceptions.SReflectException;

public class BusinessDataServiceImpl implements BusinessDataService {

    private final BusinessDataRepository businessDataRepository;

    private final JsonBusinessDataSerializer jsonBusinessDataSerializer;

    private final BusinessDataModelRepository businessDataModelRepository;

    private final TypeConverterUtil typeConverterUtil;

    public BusinessDataServiceImpl(final BusinessDataRepository businessDataRepository,
            final JsonBusinessDataSerializer jsonBusinessDataSerializer,
            BusinessDataModelRepository businessDataModelRepository, TypeConverterUtil typeConverterUtil) {
        this.businessDataRepository = businessDataRepository;
        this.jsonBusinessDataSerializer = jsonBusinessDataSerializer;
        this.businessDataModelRepository = businessDataModelRepository;
        this.typeConverterUtil = typeConverterUtil;
    }

    @Override
    public boolean isBusinessData(final Object data) {
        return isEntity(data) || isListOfEntities(data);
    }

    private boolean isListOfEntities(final Object data) {
        if (data == null) {
            return false;
        }
        if (!List.class.isAssignableFrom(data.getClass())) {
            return false;
        }
        @SuppressWarnings("rawtypes")
        final List dataList = (List) data;
        if (dataList.isEmpty()) {
            return true;
        }
        return isEntity(dataList.get(0));

    }

    private boolean isEntity(final Object data) {
        if (data == null) {
            return false;
        }
        return Entity.class.isAssignableFrom(data.getClass());
    }

    @SuppressWarnings("unchecked")
    @Override
    public Object callJavaOperation(final Object businessObject, final Object valueToSetObjectWith,
            final String methodName, final String parameterType)
            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {
        if (businessObject == null) {
            throw new SBusinessDataNotFoundException("business data is null");
        }
        if (isEntity(businessObject)) {
            return callJavaOperationOnEntity((Entity) businessObject, valueToSetObjectWith, methodName,
                    parameterType);
        }
        if (isListOfEntities(businessObject)) {
            return callJavaOperationOnEntityList((List<Entity>) businessObject, valueToSetObjectWith, methodName,
                    parameterType);
        }
        throw new SBusinessDataRepositoryException("not a business data");
    }

    private Object callJavaOperationOnEntityList(final List<Entity> businessObject,
            final Object valueToSetObjectWith, final String methodName, final String parameterType)
            throws SBusinessDataRepositoryException, SBusinessDataNotFoundException {
        try {
            invokeJavaMethod(businessObject, methodName, parameterType, valueToSetObjectWith);
            return businessObject;
        } catch (final Exception e) {
            throw new SBusinessDataRepositoryException(e);
        }
    }

    private Object callJavaOperationOnEntity(final Entity businessObject, final Object valueToSetObjectWith,
            final String methodName, final String parameterType)
            throws SBusinessDataRepositoryException, SBusinessDataNotFoundException {

        final Entity jpaEntity;
        if (businessObject.getPersistenceId() == null) {
            jpaEntity = copyForServer(businessObject);
        } else {
            jpaEntity = businessDataRepository.findById(businessObject.getClass(),
                    businessObject.getPersistenceId());
        }
        final Object valueToSet = loadValueToSet(businessObject, valueToSetObjectWith, methodName);
        try {
            invokeJavaMethod(jpaEntity, methodName, parameterType, valueToSet);
            return copyForClient(jpaEntity);
        } catch (final Exception e) {
            throw new SBusinessDataRepositoryException(e);
        }
    }

    protected void invokeJavaMethod(final Object objectToSet, final String methodName, final String parameterType,
            final Object valueToSet) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
            InvocationTargetException {
        final JavaMethodInvoker methodInvoker = new JavaMethodInvoker();
        methodInvoker.invokeJavaMethod(parameterType, valueToSet, objectToSet, methodName, parameterType);
    }

    private Object copyForClient(final Entity jpaEntity) {
        // TODO Auto-generated method stub
        return jpaEntity;
    }

    @SuppressWarnings("unchecked")
    private Object loadValueToSet(final Entity businessObject, final Object valueToSetObjectWith,
            final String methodName) throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {
        Object valueToSet;
        if (isEntity(valueToSetObjectWith)) {
            final Type relationType = getRelationType(businessObject, methodName);
            valueToSet = getPersistedValue((Entity) valueToSetObjectWith, relationType);
        } else if (isListOfEntities(valueToSetObjectWith)) {
            final Type relationType = getRelationType(businessObject, methodName);
            valueToSet = getPersistedValues((List<Entity>) valueToSetObjectWith, relationType);
        } else {
            valueToSet = valueToSetObjectWith;
        }
        return valueToSet;
    }

    private List<Long> getPrimaryKeys(final List<Entity> entities) throws SBusinessDataNotFoundException {
        List<Long> primaryKeys;
        primaryKeys = new ArrayList<Long>();
        for (final Entity entity : entities) {
            if (entity.getPersistenceId() == null) {
                throw new SBusinessDataNotFoundException("persistenceId of business data is null");
            }
            primaryKeys.add(entity.getPersistenceId());
        }
        return primaryKeys;
    }

    private Object getPersistedValues(final List<Entity> entities, Type type)
            throws SBusinessDataNotFoundException {
        if (entities.isEmpty()) {
            return new ArrayList<Entity>();
        }
        if (Type.AGGREGATION.equals(type)) {
            return businessDataRepository.findByIds(entities.get(0).getClass(), getPrimaryKeys(entities));
        } else {
            return copyForServer(entities);
        }
    }

    private Entity getPersistedValue(final Entity entity, final Type type) throws SBusinessDataNotFoundException {
        if (Type.AGGREGATION.equals(type)) {
            return businessDataRepository.findById(entity.getClass(), entity.getPersistenceId());
        } else {
            return copyForServer(entity);
        }
    }

    private List<Entity> copyForServer(List<Entity> entities) {
        return entities;
    }

    private Entity copyForServer(final Entity entity) {
        return entity;
    }

    private Type getRelationType(final Entity businessObject, final String methodName)
            throws SBusinessDataRepositoryException {
        final String fieldName = ClassReflector.getFieldName(methodName);
        Annotation[] annotations;
        try {
            annotations = businessObject.getClass().getDeclaredField(fieldName).getAnnotations();
        } catch (final NoSuchFieldException e) {
            return null;
        } catch (final SecurityException e) {
            throw new SBusinessDataRepositoryException(e);
        }
        for (final Annotation annotation : annotations) {
            final Set<Class<? extends Annotation>> annotationKeySet = getAnnotationKeySet();
            if (annotationKeySet.contains(annotation.annotationType())) {
                try {
                    final Method cascade = annotation.getClass().getMethod("cascade");
                    CascadeType[] cascadeTypes = (CascadeType[]) cascade.invoke(annotation);
                    if (CascadeType.MERGE.equals(cascadeTypes[0])) {
                        return Type.AGGREGATION;
                    }
                    if (CascadeType.ALL.equals(cascadeTypes[0])) {
                        return Type.COMPOSITION;
                    }
                } catch (Exception e) {
                    throw new SBusinessDataRepositoryException(e);
                }
            }
        }
        return null;
    }

    private Set<Class<? extends Annotation>> getAnnotationKeySet() {
        // FIXME use custom annotation on methods
        final Set<Class<? extends Annotation>> annotationKeySet = new HashSet<Class<? extends Annotation>>();
        annotationKeySet.add(OneToOne.class);
        annotationKeySet.add(OneToMany.class);
        annotationKeySet.add(ManyToMany.class);
        annotationKeySet.add(ManyToOne.class);
        return annotationKeySet;
    }

    @Override
    public Serializable getJsonEntity(final String entityClassName, final Long identifier,
            final String businessDataURIPattern)
            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {
        final Class<? extends Entity> entityClass = loadClass(entityClassName);
        final Entity entity = businessDataRepository.findById(entityClass, identifier);
        try {
            return jsonBusinessDataSerializer.serializeEntity(entity, businessDataURIPattern);
        } catch (final Exception e) {
            throw new SBusinessDataRepositoryException(e);
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public Serializable getJsonChildEntity(final String entityClassName, final Long identifier,
            final String childFieldName, final String businessDataURIPattern)
            throws SBusinessDataNotFoundException, SBusinessDataRepositoryException {
        final Class<? extends Entity> entityClass = loadClass(entityClassName);
        final Object entity = businessDataRepository.findById(entityClass, identifier);

        Object childEntity;
        java.lang.reflect.Type getterReturnType;
        try {
            final String getterName = ClassReflector.getGetterName(childFieldName);
            childEntity = ClassReflector.invokeGetter(entity, getterName);
            getterReturnType = ClassReflector.getGetterReturnType(entityClass, getterName);
        } catch (final SReflectException e) {
            throw new SBusinessDataRepositoryException(e);
        }

        if (childEntity == null) {
            return JsonBusinessDataSerializer.EMPTY_OBJECT;
        }
        if (childEntity instanceof Entity) {
            final Entity unwrap = businessDataRepository.unwrap((Entity) childEntity);
            try {
                return jsonBusinessDataSerializer.serializeEntity(unwrap, businessDataURIPattern);
            } catch (final Exception e) {
                throw new SBusinessDataRepositoryException(e);
            }
        } else if (childEntity instanceof List) {
            final Class<?> type = (Class<?>) ((ParameterizedType) getterReturnType).getActualTypeArguments()[0];
            if (Entity.class.isAssignableFrom(type)) {
                try {
                    return jsonBusinessDataSerializer.serializeEntity((List<Entity>) childEntity,
                            businessDataURIPattern);
                } catch (final Exception e) {
                    throw new SBusinessDataRepositoryException(e);
                }
            }
        }
        return null;

    }

    @Override
    public Serializable getJsonQueryEntities(final String entityClassName, final String queryName,
            final Map<String, Serializable> parameters, final Integer startIndex, final Integer maxResults,
            final String businessDataURIPattern) throws SBusinessDataRepositoryException {
        final Class<? extends Entity> businessDataClass = loadClass(entityClassName);
        final Query queryDefinition = getQueryDefinition(entityClassName, queryName);

        final List<? extends Serializable> list = businessDataRepository.findListByNamedQuery(
                getQualifiedQueryName(businessDataClass, queryName),
                getQueryReturnType(queryDefinition, entityClassName),
                getQueryParameters(queryDefinition, parameters), startIndex, maxResults);
        try {
            return jsonBusinessDataSerializer.serializeEntity((List<Entity>) list, businessDataURIPattern);
        } catch (final Exception e) {
            throw new SBusinessDataRepositoryException(e);
        }
    }

    private Class<? extends Serializable> getQueryReturnType(Query queryDefinition, String entityClassName)
            throws SBusinessDataRepositoryException {
        String returnType = queryDefinition.getReturnType();
        if (queryReturnsMultipleResults(returnType)) {
            return loadClass(entityClassName);
        }
        try {
            return (Class<? extends Serializable>) Thread.currentThread().getContextClassLoader()
                    .loadClass(queryDefinition.getReturnType());
        } catch (ClassNotFoundException e) {
            throw new SBusinessDataRepositoryException("unable to load class " + queryDefinition.getReturnType());
        }
    }

    private boolean queryReturnsMultipleResults(String returnType) {
        return returnType.equals(List.class.getName());
    }

    private String getQualifiedQueryName(Class<? extends Entity> businessDataClass, String queryName) {
        return String.format("%s.%s", businessDataClass.getSimpleName(), queryName);
    }

    private Map<String, Serializable> getQueryParameters(Query queryDefinition,
            final Map<String, Serializable> parameters) throws SBusinessDataRepositoryException {
        Set<String> errors = new HashSet<String>();
        final Map<String, Serializable> queryParameters = new HashMap<String, Serializable>();
        for (QueryParameter queryParameter : queryDefinition.getQueryParameters()) {
            if (parameters != null && parameters.containsKey(queryParameter.getName())) {
                queryParameters.put(queryParameter.getName(),
                        convertToType(loadSerializableClass(queryParameter.getClassName()),
                                parameters.get(queryParameter.getName())));
            } else {
                errors.add(queryParameter.getName());
            }
        }
        if (!errors.isEmpty()) {
            final StringBuilder errorMessage = new StringBuilder()
                    .append("parameter(s) are missing for query named ").append(queryDefinition.getName())
                    .append(" : ");
            errorMessage.append(StringUtils.join(errors, ","));
            throw new SBusinessDataRepositoryException(errorMessage.toString());
        }
        return queryParameters;
    }

    private Serializable convertToType(Class<? extends Serializable> clazz, Serializable parameterValue) {
        return (Serializable) typeConverterUtil.convertToType(clazz, parameterValue);
    }

    private Query getQueryDefinition(String className, String queryName) throws SBusinessDataRepositoryException {
        final BusinessObjectModel businessObjectModel = businessDataModelRepository.getBusinessObjectModel();
        if (businessObjectModel != null) {
            for (BusinessObject businessObject : businessObjectModel.getBusinessObjects()) {
                if (businessObject.getQualifiedName().equals(className)) {
                    List<Query> allQueries = new ArrayList<Query>();
                    allQueries.addAll(businessObject.getQueries());
                    allQueries.addAll(BDMQueryUtil.createProvidedQueriesForBusinessObject(businessObject));
                    for (Query query : allQueries) {
                        if (query.getName().equals(queryName)) {
                            return query;
                        }
                    }
                }
            }
        }
        throw new SBusinessDataRepositoryException(
                "unable to get query " + queryName + " for business object " + className);
    }

    @SuppressWarnings("unchecked")
    protected Class<? extends Entity> loadClass(final String returnType) throws SBusinessDataRepositoryException {
        try {
            return (Class<? extends Entity>) Thread.currentThread().getContextClassLoader().loadClass(returnType);
        } catch (final ClassNotFoundException e) {
            throw new SBusinessDataRepositoryException(e);
        }
    }

    protected Class<? extends Serializable> loadSerializableClass(final String className)
            throws SBusinessDataRepositoryException {
        try {
            return (Class<? extends Serializable>) Thread.currentThread().getContextClassLoader()
                    .loadClass(className);
        } catch (final ClassNotFoundException e) {
            throw new SBusinessDataRepositoryException("unable to load class " + className);
        }
    }

}