All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.atlasmap.core.DefaultAtlasFieldActionService Maven / Gradle / Ivy

package io.atlasmap.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.atlasmap.api.AtlasConversionService;
import io.atlasmap.api.AtlasException;
import io.atlasmap.api.AtlasFieldAction;
import io.atlasmap.api.AtlasFieldActionService;
import io.atlasmap.spi.AtlasFieldActionInfo;
import io.atlasmap.v2.Action;
import io.atlasmap.v2.ActionDetail;
import io.atlasmap.v2.ActionDetails;
import io.atlasmap.v2.Actions;
import io.atlasmap.v2.Field;
import io.atlasmap.v2.FieldType;
import io.atlasmap.v2.Properties;
import io.atlasmap.v2.Property;
import io.atlasmap.v2.SimpleField;

public class DefaultAtlasFieldActionService implements AtlasFieldActionService {

    private static final Logger LOG = LoggerFactory.getLogger(DefaultAtlasFieldActionService.class);
    private ActionDetails actionDetails = new ActionDetails();
    private AtlasConversionService conversionService = null;

    public DefaultAtlasFieldActionService(AtlasConversionService conversionService) {
        this.conversionService = conversionService;
    }

    public void init() {
        loadFieldActions();
    }

    protected void loadFieldActions() {
        ClassLoader classLoader = this.getClass().getClassLoader();
        final ServiceLoader fieldActionServiceLoader = ServiceLoader.load(AtlasFieldAction.class, classLoader);
        for (final AtlasFieldAction atlasFieldAction : fieldActionServiceLoader) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Loading FieldAction class: " + atlasFieldAction.getClass().getCanonicalName());
            }

            Class clazz = atlasFieldAction.getClass();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                AtlasFieldActionInfo annotation = method.getAnnotation(AtlasFieldActionInfo.class);
                if (annotation != null) {
                    ActionDetail det = new ActionDetail();
                    det.setClassName(clazz.getName());
                    det.setMethod(method.getName());
                    det.setName(annotation.name());
                    det.setSourceType(annotation.sourceType());
                    det.setTargetType(annotation.targetType());
                    det.setSourceCollectionType(annotation.sourceCollectionType());
                    det.setTargetCollectionType(annotation.targetCollectionType());

                    try {
                        det.setParameters(detectFieldActionParameters("io.atlasmap.v2." + annotation.name()));
                    } catch (ClassNotFoundException e) {
                        LOG.error(String.format("Error detecting parameters for field action=%s msg=%s", annotation.name(), e.getMessage()), e);
                    }

                    if (LOG.isTraceEnabled()) {
                        LOG.trace("Loaded FieldAction: " + det.getName());
                    }
                    listActionDetails().add(det);
                }
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("Loaded %s Field Actions", listActionDetails().size()));
        }
    }

    @Override
    public List listActionDetails() {
        return actionDetails.getActionDetail();
    }

    /*
     * TODO: getActionDetailByActionName() when all references are updated to use
     *
     * ActionDetail = findActionDetail(String actionName, FieldType sourceType)
     *
     * ref: https://github.com/atlasmap/atlasmap-runtime/issues/216
     */
    @Deprecated
    protected ActionDetail getActionDetailByActionName(String actionName) {
        for(ActionDetail actionDetail : listActionDetails()) {
            if(actionDetail.getName().equals(actionName)) {
                return actionDetail;
            }
        }

        return null;
    }

    /*
     * 1. Find FieldAction by name
     * 2. If multiple matches are found, return the best one based on FieldType sourceType
     * 3. If there is not an exact match to sourceType, return the first FieldAction
     * 4. If no matches found, return null
     *
     *
     * @param actionName The name of the FieldAction
     * @param sourceType A hint used to determine which FieldAction to use
     *                   when multiple FieldActions exist with the same name
     *
     * @return ActionDetail
     */
    protected ActionDetail findActionDetail(String actionName, FieldType sourceType) {

        List matches = new ArrayList();
        for(ActionDetail actionDetail : listActionDetails()) {
            if(actionDetail.getName().equals(actionName)) {
                matches.add(actionDetail);
            }
        }

        switch(matches.size()) {
        case 0: return null;
        case 1: return matches.get(0);
        default:
            if(sourceType != null && !Arrays.asList(FieldType.ALL, FieldType.NONE).contains(sourceType)) {
                for(ActionDetail actionDetail : matches) {
                    if(sourceType.equals(actionDetail.getSourceType())) {
                        return actionDetail;
                    }
                }
            }
            return matches.get(0);
        }
    }

    @Override
    public void processActions(Actions actions, Field field) throws AtlasException {
        Field tmpField = internalProcessActions(actions, field.getValue(), field.getFieldType());
        field.setValue(tmpField.getValue());
        field.setFieldType(tmpField.getFieldType());
    }

    @Override
    public Object processActions(Actions actions, Object sourceObject, FieldType targetType) throws AtlasException {
        Field tmpField = internalProcessActions(actions, sourceObject, targetType);
        if(tmpField == null) {
            return null;
        }

        if(tmpField.getFieldType() != null && !tmpField.getFieldType().equals(targetType)) {
            tmpField.setValue(getConversionService().convertType(tmpField.getValue(), tmpField.getFieldType(), targetType));
        }

        return tmpField.getValue();
    }

    protected Field internalProcessActions(Actions actions, Object sourceObject, FieldType targetType) throws AtlasException {

        Field processedField = new SimpleField();
        processedField.setValue(sourceObject);
        processedField.setFieldType(targetType);

        if(FieldType.COMPLEX.equals(targetType)) {
            return processedField;
        }

        Object tmpSourceObject = sourceObject;
        FieldType sourceType = (sourceObject != null ? getConversionService().fieldTypeFromClass(sourceObject.getClass()) : FieldType.NONE);

        if(actions == null || actions.getActions() == null || actions.getActions().isEmpty()) {
            if(sourceObject == null) {
                return processedField;
            }
            processedField.setValue(getConversionService().convertType(sourceObject, sourceType, targetType));
            processedField.setFieldType(targetType);
            return processedField;
        }

        FieldType currentType = sourceType;
        for(Action action : actions.getActions()) {
            ActionDetail detail = findActionDetail(action.getDisplayName(), currentType);
            if(!detail.getSourceType().equals(currentType) && !FieldType.ALL.equals(detail.getSourceType())) {
                tmpSourceObject = getConversionService().convertType(sourceObject, currentType, detail.getSourceType());
            }

            processedField.setValue(processAction(action, detail, tmpSourceObject));
            processedField.setFieldType(detail.getTargetType());
            currentType = detail.getTargetType();
        }

        return processedField;
    }

    protected Object processAction(Action action, ActionDetail actionDetail, Object sourceObject) throws AtlasException {
        Object targetObject = null;
        if(actionDetail != null) {
            Object actionObject = null;
            try {
                Class actionClazz = Class.forName(actionDetail.getClassName());
                actionObject = actionClazz.newInstance();

                Method method =  null;
                if(actionDetail.getSourceType() != null) {
                    switch(actionDetail.getSourceType()) {
                    case BOOLEAN: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Boolean.class); break;
                    case BYTE: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Byte.class); break;
                    case BYTE_ARRAY: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Byte[].class); break;
                    case CHAR: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Character.class); break;
                    case DOUBLE: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Double.class); break;
                    case FLOAT: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Float.class); break;
                    case INTEGER: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Integer.class); break;
                    case LONG: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Long.class); break;
                    case SHORT: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Short.class); break;
                    case STRING: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, String.class); break;
                    case ALL: method = actionClazz.getMethod(actionDetail.getMethod(), Action.class, Object.class); break;
                    default:
                        LOG.warn(String.format("Unsupported sourceType=%s in actionClass=%s", actionDetail.getSourceType().value(), actionDetail.getClassName()));
                        break;
                    }
                }

                if(method == null) {
                    throw new AtlasException(String.format("Unable to locate field action className=%s method=%s sourceType=%s", actionDetail.getClassName(), actionDetail.getMethod(), actionDetail.getSourceType()));
                }

                if(Modifier.isStatic(method.getModifiers())) {
                    targetObject = method.invoke(null, action, sourceObject);
                } else {
                    targetObject = method.invoke(actionObject, action, sourceObject);
                }
            } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | SecurityException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException e) {
                throw new AtlasException(String.format("Error processing action %s", actionDetail.getName()), e);
            }
            return targetObject;
        }
        return sourceObject;
    }

    protected Properties detectFieldActionParameters(String actionClassName) throws ClassNotFoundException {
        Class actionClazz = Class.forName(actionClassName);

        Properties props = null;
        for(Method method : actionClazz.getMethods()) {
            // Find setters to avoid the get / is confusion
            if(method.getParameterCount() == 1 && method.getName().startsWith("set")) {
                // We have a parameter
                if(props == null) {
                    props = new Properties();
                }

                Property prop = null;
                for(Parameter param : method.getParameters()) {
                    prop = new Property();
                    prop.setName(camelize(method.getName().substring("set".length())));
                    prop.setFieldType(getConversionService().fieldTypeFromClass(param.getType()));
                    props.getProperty().add(prop);
                }
            }
        }

        return props;
    }

    public AtlasConversionService getConversionService() {
        return this.conversionService;
    }

    public static String camelize(String parameter) {
        if (parameter == null || parameter.length() == 0) {
            return parameter;
        }
        char c[] = parameter.toCharArray();
        c[0] = Character.toLowerCase(c[0]);
        return new String(c);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy