com.effektif.workflow.impl.workflowinstance.ActivityInstanceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.effektif.workflow.impl.workflowinstance.ActivityInstanceImpl.java

Source

/*
 * Copyright 2014 Effektif GmbH.
 *
 * Licensed 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 com.effektif.workflow.impl.workflowinstance;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.effektif.workflow.impl.job.Job;

import org.joda.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.effektif.workflow.api.WorkflowEngine;
import com.effektif.workflow.api.model.RelativeTime;
import com.effektif.workflow.api.model.TypedValue;
import com.effektif.workflow.api.model.WorkflowInstanceId;
import com.effektif.workflow.api.workflowinstance.ActivityInstance;
import com.effektif.workflow.impl.conditions.ConditionImpl;
import com.effektif.workflow.impl.data.DataTypeImpl;
import com.effektif.workflow.impl.data.TypedValueImpl;
import com.effektif.workflow.impl.data.types.ListTypeImpl;
import com.effektif.workflow.impl.util.Lists;
import com.effektif.workflow.impl.util.Time;
import com.effektif.workflow.impl.workflow.ActivityImpl;
import com.effektif.workflow.impl.workflow.BindingImpl;
import com.effektif.workflow.impl.workflow.InputParameterImpl;
import com.effektif.workflow.impl.workflow.TransitionImpl;

/**
 * @author Tom Baeyens
 */
public class ActivityInstanceImpl extends ScopeInstanceImpl {

    public static final String STATE_STARTING = "starting";
    public static final String STATE_STARTING_MULTI_CONTAINER = "startingMultiParent";
    public static final String STATE_STARTING_MULTI_INSTANCE = "startingMultiInstance";
    public static final String STATE_PROPAGATE_TO_PARENT = "propagateToParent";
    public static final String STATE_JOINING = "joining";

    /** @see WorkflowInstanceImpl#isWorkAsync(ActivityInstanceImpl) */
    public static final Set<String> START_WORKSTATES = new HashSet<>(
            Lists.of(STATE_STARTING, STATE_STARTING_MULTI_CONTAINER, STATE_STARTING_MULTI_INSTANCE));

    public static final Logger log = LoggerFactory.getLogger(WorkflowEngine.class);

    public String id;
    public ActivityImpl activity;
    public String workState;
    public WorkflowInstanceId calledWorkflowInstanceId;
    public List<String> transitionsTaken;

    public ActivityInstanceImpl() {
    }

    public ActivityInstanceImpl(ScopeInstanceImpl parent, ActivityImpl activity, String id) {
        super(parent, activity);
        this.id = id;
        this.activity = activity;
    }

    public ActivityInstance toActivityInstance() {
        return toActivityInstance(false);
    }

    public ActivityInstance toActivityInstance(boolean includeWorkState) {
        ActivityInstance activityInstance = new ActivityInstance();
        activityInstance.setId(id);
        activityInstance.setActivityId(activity.id);
        activityInstance.setCalledWorkflowInstanceId(calledWorkflowInstanceId);
        toScopeInstance(activityInstance, includeWorkState);
        if (includeWorkState) {
            activityInstance.setPropertyOpt("workState", workState);
        }
        return activityInstance;
    }

    @Override
    protected String getActivityInstanceId() {
        return id;
    }

    public void execute() {
        if (workflow.workflowEngine.notifyActivityInstanceStarted(this)) {
            activity.activityType.execute(this);
        }
    }

    /** Default BPMN logic when an activity ends */
    @Override
    public void onwards() {
        if (log.isDebugEnabled()) {
            log.debug("Onwards " + this);
        }

        // First we end the activity instance.  
        // Subsequent invocations to end will be ignored.
        end();

        boolean isTransitionTaken = false;

        // Take each outgoing transition 'in parallel'
        // Note that process concurrency is not the same as java multithreaded computation
        if (activity.hasOutgoingTransitions()) {
            for (TransitionImpl transition : activity.outgoingTransitions) {
                // Only take a transition if there is no condition or if the condition resolves to true.
                ConditionImpl condition = transition.condition;
                if (condition != null ? condition.eval(this) : true) {
                    isTransitionTaken = true;
                    takeTransition(transition);
                }
            }
        }

        // if there were no outgoing transitions or none of them could be taken, 
        if (!isTransitionTaken) {
            // propagate the execution flow upwards to the parent
            propagateToParent();
        }
    }

    public void endAndPropagateToParent() {
        end();
        propagateToParent();
    }

    public void end() {
        if (end == null) {
            if (hasOpenActivityInstances()) {
                throw new RuntimeException(
                        "Can't end this activity instance. There are open activity instances: " + this);
            }
            setEnd(Time.now());
            workflow.workflowEngine.notifyActivityInstanceEnded(this);
            destroyScopeInstance();
            setWorkState(null);
        }
    }

    public void propagateToParent() {
        setWorkState(STATE_PROPAGATE_TO_PARENT);
        workflowInstance.addWork(this);
    }

    public void setWorkState(String workState) {
        // log.debug("Setting workstate of "+this+" from "+this.workState+" to "+workState);
        this.workState = workState;
        if (updates != null) {
            getUpdates().isWorkStateChanged = true;
            if (parent != null) {
                parent.propagateActivityInstanceChange();
            }
        }
    }

    public void setJoining() {
        setWorkState(STATE_JOINING);
    }

    public boolean isJoining() {
        return STATE_JOINING.equals(workState);
    }

    public void removeJoining(ActivityInstanceImpl activityInstance) {
        activityInstance.setWorkState(null);
    }

    /** Starts the to (destination) activity in the current (parent) scope.
     * This methods will also end the current activity instance.
     * This method can be called multiple times in one start() */
    public void takeTransition(TransitionImpl transition) {
        if (transition.id != null || activity.activityType.saveTransitionsTaken()) {
            addTransitionTaken(transition.id);
        }
        ActivityInstanceImpl toActivityInstance = null;
        ActivityImpl to = transition.to;
        if (to != null) {
            end();
            if (log.isDebugEnabled()) {
                log.debug("Taking transition to " + to);
            }
            toActivityInstance = parent.createActivityInstance(to);
        } else {
            log.debug("Dangling transition.  Propagating to parent.");
            end();
            propagateToParent();
        }
        workflow.workflowEngine.notifyTransitionTaken(this, transition, toActivityInstance);
    }

    protected void addTransitionTaken(String transitionId) {
        if (transitionsTaken == null) {
            transitionsTaken = new ArrayList<>();
        }
        transitionsTaken.add(transitionId);
        if (updates != null) {
            getUpdates().isTransitionsTakenChanged = true;
            if (parent != null) {
                parent.propagateActivityInstanceChange();
            }
        }
    }

    @Override
    public ActivityInstanceImpl findActivityInstance(String activityInstanceId) {
        if (activityInstanceId == null) {
            return null;
        }
        if (activityInstanceId.equals(this.id)) {
            return this;
        }
        return super.findActivityInstance(activityInstanceId);
    }

    public ActivityImpl getActivity() {
        return activity;
    }

    public void setActivity(ActivityImpl activityDefinition) {
        this.activity = activityDefinition;
    }

    public String toString() {
        String activityTypeName = activity.activityType.getActivityApiClass().getSimpleName();
        String activityId = activity.id;
        String activityName = activity.activity.getName();
        return "(" + activityTypeName + "|" + (activityName != null ? activityName + "|" : "")
                + (activityId != null ? activityId + "|" : "") + id + ")";
    }

    public void setEnd(LocalDateTime end) {
        this.end = end;
        if (start != null && end != null) {
            this.duration = end.toDate().getTime() - start.toDate().getTime();
        }
        if (updates != null) {
            updates.isEndChanged = true;
            if (parent != null) {
                parent.propagateActivityInstanceChange();
            }
        }
    }

    @Override
    public ActivityInstanceImpl findActivityInstanceByActivityId(String activityDefinitionId) {
        if (activityDefinitionId == null) {
            return null;
        }
        if (activityDefinitionId.equals(activity.id)) {
            return this;
        }
        return super.findActivityInstanceByActivityId(activityDefinitionId);
    }

    @Override
    public boolean isWorkflowInstance() {
        return false;
    }

    public void setCalledWorkflowInstanceId(WorkflowInstanceId calledWorkflowInstanceId) {
        this.calledWorkflowInstanceId = calledWorkflowInstanceId;
    }

    public WorkflowInstanceId getCalledWorkflowInstanceId() {
        return calledWorkflowInstanceId;
    }

    @Override
    public ActivityInstanceUpdates getUpdates() {
        return (ActivityInstanceUpdates) updates;
    }

    public void trackUpdates(boolean isNew) {
        if (updates == null) {
            updates = new ActivityInstanceUpdates(isNew);
        } else {
            updates.reset(isNew);
        }
        super.trackUpdates(isNew);
    }

    public boolean hasActivityInstance(String activityInstanceId) {
        if (id != null && id.equals(activityInstanceId)) {
            return true;
        }
        return super.hasActivityInstance(activityInstanceId);
    }

    public String getId() {
        return id;
    }

    // TODO add the expected type for conversion?
    public <T> T getInputValue(String key) {
        if (activity == null || activity.activityType == null || activity.activityType.getInputs() == null) {
            return null;
        }
        InputParameterImpl parameter = (InputParameterImpl) activity.activityType.getInputs().get(key);
        TypedValueImpl typedValue = getInputTypedValue(parameter);
        return (T) (typedValue != null ? typedValue.value : null);
    }

    protected TypedValueImpl getInputTypedValue(InputParameterImpl parameter) {
        if (parameter != null) {
            if (parameter.binding != null) {
                return getTypedValue(parameter.binding);
            }
            if (parameter.bindings != null) {
                DataTypeImpl<?> listType = null;
                List<Object> values = new ArrayList<>();
                for (BindingImpl<?> binding : parameter.bindings) {
                    TypedValueImpl typedValue = getTypedValue(binding);
                    if (typedValue != null) {
                        if (typedValue.getValue() instanceof Collection) {
                            if (listType == null && typedValue.getType() != null) {
                                listType = typedValue.getType();
                            }
                            Collection value = (Collection) typedValue.value;
                            if (value != null) {
                                values.addAll(value);
                            }
                        } else {
                            if (listType == null && typedValue.getType() != null) {
                                listType = new ListTypeImpl(typedValue.getType());
                            }
                            values.add(typedValue.value);
                        }
                    }
                }
                return new TypedValueImpl(listType, values);
            }
        }
        return null;
    }

    public Map<String, TypedValueImpl> getInputValueImpls() {
        Map<String, TypedValueImpl> inputValues = new HashMap<>();
        if (activity != null && activity.activityType != null && activity.activityType.getInputs() != null) {
            Map<String, InputParameterImpl> inputs = activity.activityType.getInputs();
            for (String inputKey : inputs.keySet()) {
                InputParameterImpl inputParameter = inputs.get(inputKey);
                TypedValueImpl inputValue = getInputTypedValue(inputParameter);
                if (inputValue != null) {
                    inputValues.put(inputKey, inputValue);
                }
            }
        }
        return inputValues;
    }

    public Map<String, TypedValue> getInputValues() {
        Map<String, TypedValue> inputValues = new HashMap<>();
        Map<String, TypedValueImpl> inputValueImpls = getInputValueImpls();
        if (inputValueImpls != null) {
            for (String key : inputValueImpls.keySet()) {
                TypedValueImpl typedValueImpl = inputValueImpls.get(key);
                inputValues.put(key, typedValueImpl.toTypedValue());
            }
        }
        return inputValues;
    }
}