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

com.amazonaws.services.simpleworkflow.flow.pojo.POJOActivityImplementationFactory Maven / Gradle / Ivy

Go to download

The Amazon Web Services SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

The newest version!
/*
 * Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"). You may not
 * use this file except in compliance with the License. A copy of the License is
 * located at
 * 
 * http://aws.amazon.com/apache2.0
 * 
 * or in the "license" file accompanying this file. This file 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 com.amazonaws.services.simpleworkflow.flow.pojo;

import java.lang.reflect.Method;
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 com.amazonaws.services.simpleworkflow.flow.DataConverter;
import com.amazonaws.services.simpleworkflow.flow.JsonDataConverter;
import com.amazonaws.services.simpleworkflow.flow.annotations.Activities;
import com.amazonaws.services.simpleworkflow.flow.annotations.Activity;
import com.amazonaws.services.simpleworkflow.flow.annotations.ActivityRegistrationOptions;
import com.amazonaws.services.simpleworkflow.flow.annotations.ManualActivityCompletion;
import com.amazonaws.services.simpleworkflow.flow.annotations.NullDataConverter;
import com.amazonaws.services.simpleworkflow.flow.annotations.SkipTypeRegistration;
import com.amazonaws.services.simpleworkflow.flow.common.FlowConstants;
import com.amazonaws.services.simpleworkflow.flow.generic.ActivityImplementation;
import com.amazonaws.services.simpleworkflow.flow.generic.ActivityImplementationFactory;
import com.amazonaws.services.simpleworkflow.flow.worker.ActivityTypeExecutionOptions;
import com.amazonaws.services.simpleworkflow.flow.worker.ActivityTypeRegistrationOptions;
import com.amazonaws.services.simpleworkflow.model.ActivityType;

public class POJOActivityImplementationFactory extends ActivityImplementationFactory {

    private static class ParentInterfaceOptions {

        private String version;

        private String prefix;

        private ActivityRegistrationOptions registrationOptions;

        private boolean skipRegistration;

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getPrefix() {
            return prefix;
        }

        public void setPrefix(String prefix) {
            this.prefix = prefix;
        }

        public ActivityRegistrationOptions getRegistrationOptions() {
            return registrationOptions;
        }

        public void setRegistrationOptions(ActivityRegistrationOptions options) {
            if (options != null) {
                setSkipRegistration(false);
            }
            this.registrationOptions = options;
        }

        public boolean isSkipRegistration() {
            return skipRegistration;
        }

        public void setSkipRegistration(boolean skipRegistration) {
            if (skipRegistration) {
                registrationOptions = null;
            }
            this.skipRegistration = skipRegistration;
        }

    }

    private List activityTypesToRegister = new ArrayList();

    private Map implementationsMap = new HashMap();

    private DataConverter dataConverter = new JsonDataConverter();

    public POJOActivityImplementationFactory() {
        super();
    }

    public POJOActivityImplementationFactory(Iterable activityImplementationObjects)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        this();
        addActivitiesImplementations(activityImplementationObjects, null);
    }

    public POJOActivityImplementationFactory(Iterable activityImplementationObjects, DataConverter dataConverter)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        this();
        addActivitiesImplementations(activityImplementationObjects, dataConverter);
    }

    public DataConverter getDataConverter() {
        return dataConverter;
    }

    public void setDataConverter(DataConverter dataConverter) {
        if (dataConverter == null) {
            throw new IllegalArgumentException("null dataConverter");
        }
        this.dataConverter = dataConverter;
    }

    public void setActivitiesImplementations(Iterable activitiesImplementations)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        addActivitiesImplementations(activitiesImplementations, null);
    }

    public Iterable getActivitiesImplementations() {
        List result = new ArrayList();
        for (POJOActivityImplementation impl : implementationsMap.values()) {
            result.add(impl.getActivitiesImplementation());
        }
        return result;
    }

    public List addActivitiesImplementations(Iterable activitiesImplementations)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        return addActivitiesImplementations(activitiesImplementations, null);
    }

    public List addActivitiesImplementations(Iterable activitiesImplementations, DataConverter dataConverter)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        List result = new ArrayList();
        for (Object activityImplementationObject : activitiesImplementations) {
            result.addAll(addActivitiesImplementation(activityImplementationObject, dataConverter));
        }
        return result;
    }

    public List addActivitiesImplementation(Object activitiesImplementation)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        return addActivitiesImplementation(activitiesImplementation, null);
    }

    public List addActivitiesImplementation(Object activitiesImplementation, DataConverter converter)
            throws InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException {
        if (activitiesImplementation == null) {
            throw new IllegalArgumentException("activitiesImplementation is null.");
        }
        Set result = new HashSet();
        Set> activitiesInterfaces = new HashSet>();
        getImplementedInterfacesAnnotatedWithActivities(activitiesImplementation.getClass(), activitiesInterfaces);
        if (activitiesInterfaces.size() == 0) {
            throw new IllegalArgumentException(
                    "Activity implementation object does not implement any interface annotated with @Activities: "
                            + activitiesImplementation.getClass());
        }
        for (Class interfaze : activitiesInterfaces) {
            Map methods = new HashMap();
            ParentInterfaceOptions parentOptions = new ParentInterfaceOptions();
            addActivities(activitiesImplementation, interfaze, methods, parentOptions, converter, result);
        }
        return new ArrayList(result);
    }

    private void addActivities(Object implementation, Class interfaze, Map methods,
            ParentInterfaceOptions parentOptions, DataConverter converter, Set addedTypes)
            throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Activities activitiesAnnotation = interfaze.getAnnotation(Activities.class);
        for (Class parent : interfaze.getInterfaces()) {
            addActivities(implementation, parent, methods, parentOptions, converter, addedTypes);
        }
        if (activitiesAnnotation != null) {
            String interfaceName = interfaze.getSimpleName();
            if (!nullOrEmpty(activitiesAnnotation.activityNamePrefix())) {
                parentOptions.setPrefix(activitiesAnnotation.activityNamePrefix());
            }
            if (!nullOrEmpty(activitiesAnnotation.version())) {
                parentOptions.setVersion(activitiesAnnotation.version());
            }
            converter = (converter == null) ? createConverter(activitiesAnnotation.dataConverter()) : converter;
            ActivityRegistrationOptions interfaceRegistrationOptionsAnnotation = interfaze.getAnnotation(ActivityRegistrationOptions.class);
            SkipTypeRegistration interfaceSkipTypeRegistrationAnnotation = interfaze.getAnnotation(SkipTypeRegistration.class);
            if (interfaceRegistrationOptionsAnnotation != null) {
                if (interfaceSkipTypeRegistrationAnnotation != null) {
                    throw new IllegalArgumentException(
                            "@ActivityRegistrationOptions is not allowed for the interface annotated with @SkipTypeRegistration.");

                }
                parentOptions.setRegistrationOptions(interfaceRegistrationOptionsAnnotation);
            }
            else if (interfaceSkipTypeRegistrationAnnotation != null) {
                parentOptions.setSkipRegistration(true);
            }
            for (Method method : interfaze.getMethods()) {
                if (!method.getDeclaringClass().equals(interfaze)) {
                    continue;
                }
                Activity activityAnnotation = method.getAnnotation(Activity.class);
                ActivityType activityType = getActivityType(interfaceName, method, activityAnnotation, parentOptions);
                ActivityRegistrationOptions registrationOptionsAnnotation = method.getAnnotation(ActivityRegistrationOptions.class);
                SkipTypeRegistration skipTypeRegistrationAnnotation = interfaze.getAnnotation(SkipTypeRegistration.class);
                ActivityTypeRegistrationOptions registrationOptions = null;
                if (skipTypeRegistrationAnnotation != null) {
                    if (registrationOptionsAnnotation != null) {
                        throw new IllegalArgumentException(
                                "@ActivityRegistrationOptions is not allowed for the method annotated with @SkipTypeRegistration: "
                                        + method);
                    }
                }
                else {
                    if (registrationOptionsAnnotation != null || parentOptions.getRegistrationOptions() != null) {
                        POJOActivityImplementation existingImplementation = implementationsMap.get(activityType);
                        if (existingImplementation != null && !addedTypes.contains(activityType)) {
                            String message = "Duplicate declaration for activity type=" + activityType.getName() + ", version="
                                    + activityType.getVersion() + ": " + existingImplementation.getMethod() + " and " + method;
                            throw new IllegalArgumentException(message);
                        }
                        registrationOptions = createRegistrationOptions(registrationOptionsAnnotation,
                                parentOptions.getRegistrationOptions());
                    }
                    else if (!parentOptions.isSkipRegistration()) {
                        throw new IllegalArgumentException(
                                "No @ActivityRegistationOptions found either on interface or method for " + method);
                    }
                }
                //TODO: support methods defined in parents as well as overrides
                if (!addedTypes.contains(activityType)) {
                    Method activityImplementationMethod = implementation.getClass().getMethod(method.getName(),
                            method.getParameterTypes());
                    ActivityTypeExecutionOptions executionOptions = createExecutionOptions(activityType, activityImplementationMethod);

                    
                    POJOActivityImplementation activityImplementation = new POJOActivityImplementation(implementation, method,
                            registrationOptions, executionOptions, converter);
                    activityTypesToRegister.add(activityType);
                    addedTypes.add(activityType);
                    implementationsMap.put(activityType, activityImplementation);
                }
            }
        }

    }

    @Override
    public Iterable getActivityTypesToRegister() {
        return activityTypesToRegister;
    }

    @Override
    public ActivityImplementation getActivityImplementation(ActivityType activityType) {
        return implementationsMap.get(activityType);
    }

    private DataConverter createConverter(Class converterType)
            throws InstantiationException, IllegalAccessException {
        if (converterType == null || converterType.equals(NullDataConverter.class)) {
            return dataConverter == null ? new JsonDataConverter() : dataConverter;
        }
        return converterType.newInstance();
    }

    private static ActivityType getActivityType(String interfaceName, Method activity, Activity activityAnnotation,
            ParentInterfaceOptions parentOptions) {
        ActivityType activityType = new ActivityType();

        String activityName = null;
        String activityVersion = null;
        if (activityAnnotation != null) {
            if (!nullOrEmpty(activityAnnotation.name())) {
                activityName = activityAnnotation.name();
            }
            if (!nullOrEmpty(activityAnnotation.version())) {
                activityVersion = activityAnnotation.version();
            }

        }
        if (activityName == null) {
            if (!nullOrEmpty(parentOptions.getPrefix())) {
                activityName = parentOptions.getPrefix() + activity.getName();
            }
            else {
                activityName = interfaceName + "." + activity.getName();
            }
        }
        if (activityVersion == null) {
            if (!nullOrEmpty(parentOptions.getVersion())) {
                activityVersion = parentOptions.getVersion();
            }
            else {
                throw new IllegalArgumentException("No version found for activity defined by " + activity);
            }
        }

        activityType.setName(activityName);
        activityType.setVersion(activityVersion);
        return activityType;
    }

    private static boolean nullOrEmpty(String nameFromAnnotation) {
        return nameFromAnnotation == null || nameFromAnnotation.isEmpty();
    }

    /**
     * Recursively find all interfaces annotated with @Activities that given
     * class implements. Do not include interfaces that @Activities annotated
     * interface extends.
     */
    private void getImplementedInterfacesAnnotatedWithActivities(Class implementationType, Set> implementedInterfaces) {
        Class superClass = implementationType.getSuperclass();
        if (superClass != null) {
            getImplementedInterfacesAnnotatedWithActivities(superClass, implementedInterfaces);
        }
        
        Class[] interfaces = implementationType.getInterfaces();
        for (Class i : interfaces) {
            if (i.getAnnotation(Activities.class) != null && !implementedInterfaces.contains(i)) {
                boolean skipAdd = removeSuperInterfaces(i, implementedInterfaces);
                if (!skipAdd) {
                    implementedInterfaces.add(i);
                }
            }
            else {
                getImplementedInterfacesAnnotatedWithActivities(i, implementedInterfaces);
            }
        }
    }
    
    private boolean removeSuperInterfaces(Class interfaceToAdd, Set> implementedInterfaces) {
        boolean skipAdd = false;
        List> interfacesToRemove = new ArrayList>();
        for (Class addedInterface : implementedInterfaces) {
            if (addedInterface.isAssignableFrom(interfaceToAdd)) {
                interfacesToRemove.add(addedInterface);
            }
            if (interfaceToAdd.isAssignableFrom(addedInterface)) {
                skipAdd = true;
            }
        }
        
        for (Class interfaceToRemove : interfacesToRemove) {
            implementedInterfaces.remove(interfaceToRemove);
        }
        
        return skipAdd;
    }

    private static ActivityTypeRegistrationOptions createRegistrationOptions(ActivityRegistrationOptions registrationOptions,
            ActivityRegistrationOptions parentRegistrationOptions) {

        ActivityRegistrationOptions registrationOptionsAnnotation = registrationOptions != null ? registrationOptions
                : parentRegistrationOptions;

        ActivityTypeRegistrationOptions result = new ActivityTypeRegistrationOptions();

        result.setDescription(emptyStringToNull(registrationOptionsAnnotation.description()));

        long taskHeartbeatTimeoutSeconds = registrationOptionsAnnotation.defaultTaskHeartbeatTimeoutSeconds();
        if (taskHeartbeatTimeoutSeconds > FlowConstants.USE_REGISTERED_DEFAULTS) {
            result.setDefaultTaskHeartbeatTimeoutSeconds(taskHeartbeatTimeoutSeconds);
        }

        long taskScheduleToCloseTimeoutSeconds = registrationOptionsAnnotation.defaultTaskScheduleToCloseTimeoutSeconds();
        if (taskScheduleToCloseTimeoutSeconds > FlowConstants.USE_REGISTERED_DEFAULTS) {
            result.setDefaultTaskScheduleToCloseTimeoutSeconds(taskScheduleToCloseTimeoutSeconds);
        }

        long taskScheduleToStartTimeoutSeconds = registrationOptionsAnnotation.defaultTaskScheduleToStartTimeoutSeconds();
        if (taskScheduleToStartTimeoutSeconds > FlowConstants.USE_REGISTERED_DEFAULTS) {
            result.setDefaultTaskScheduleToStartTimeoutSeconds(taskScheduleToStartTimeoutSeconds);
        }

        long taskStartToCloseTimeoutSeconds = registrationOptionsAnnotation.defaultTaskStartToCloseTimeoutSeconds();
        if (taskStartToCloseTimeoutSeconds > FlowConstants.USE_REGISTERED_DEFAULTS) {
            result.setDefaultTaskStartToCloseTimeoutSeconds(taskStartToCloseTimeoutSeconds);
        }

        String taskList = registrationOptionsAnnotation.defaultTaskList();
        if (!taskList.equals(FlowConstants.USE_WORKER_TASK_LIST)) {
            result.setDefaultTaskList(taskList);
        }
        else if (taskList.equals(FlowConstants.NO_DEFAULT_TASK_LIST)) {
            result.setDefaultTaskList(null);
        }

        return result;
    }

    private static ActivityTypeExecutionOptions createExecutionOptions(ActivityType activityType, Method activityImplementation) {
        assert (activityType != null);

        ActivityTypeExecutionOptions executionOptions = new ActivityTypeExecutionOptions();
        if (activityImplementation != null) {
            ManualActivityCompletion manualCompletion = activityImplementation.getAnnotation(ManualActivityCompletion.class);
            executionOptions.setManualActivityCompletion(manualCompletion != null);
        }

        return executionOptions;
    }

    private static String emptyStringToNull(String value) {
        if (value.length() == 0) {
            return null;
        }
        return value;
    }

}