com.catify.processengine.core.nodes.NodeFactoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.catify.processengine.core.nodes.NodeFactoryImpl.java

Source

/**
 * *******************************************************
 * Copyright (C) 2013 catify <info@catify.com>
 * *******************************************************
 *
 * 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.catify.processengine.core.nodes;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.xml.bind.JAXBElement;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.UntypedActorFactory;

import com.catify.processengine.core.data.dataobjects.DataObjectHandling;
import com.catify.processengine.core.data.dataobjects.DataObjectIdService;
import com.catify.processengine.core.data.dataobjects.NoDataObjectSP;
import com.catify.processengine.core.data.services.IdService;
import com.catify.processengine.core.nodes.eventdefinition.EventDefinitionParameter;
import com.catify.processengine.core.nodes.loops.LoopStrategy;
import com.catify.processengine.core.nodes.loops.LoopStrategyFactory;
import com.catify.processengine.core.processdefinition.jaxb.TBoundaryEvent;
import com.catify.processengine.core.processdefinition.jaxb.TCatchEvent;
import com.catify.processengine.core.processdefinition.jaxb.TComplexGateway;
import com.catify.processengine.core.processdefinition.jaxb.TEndEvent;
import com.catify.processengine.core.processdefinition.jaxb.TEventBasedGateway;
import com.catify.processengine.core.processdefinition.jaxb.TExclusiveGateway;
import com.catify.processengine.core.processdefinition.jaxb.TExpression;
import com.catify.processengine.core.processdefinition.jaxb.TFlowElement;
import com.catify.processengine.core.processdefinition.jaxb.TFlowNode;
import com.catify.processengine.core.processdefinition.jaxb.TIntermediateCatchEvent;
import com.catify.processengine.core.processdefinition.jaxb.TIntermediateThrowEvent;
import com.catify.processengine.core.processdefinition.jaxb.TParallelGateway;
import com.catify.processengine.core.processdefinition.jaxb.TProcess;
import com.catify.processengine.core.processdefinition.jaxb.TReceiveTask;
import com.catify.processengine.core.processdefinition.jaxb.TSendTask;
import com.catify.processengine.core.processdefinition.jaxb.TSequenceFlow;
import com.catify.processengine.core.processdefinition.jaxb.TServiceTask;
import com.catify.processengine.core.processdefinition.jaxb.TStartEvent;
import com.catify.processengine.core.processdefinition.jaxb.TSubProcess;
import com.catify.processengine.core.processdefinition.jaxb.TTerminateEventDefinition;
import com.catify.processengine.core.services.ActorReferenceService;

/**
 * A factory for creating akka node objects.
 * 
 * @author christopher kster
 * 
 */
@Repository
public class NodeFactoryImpl implements NodeFactory {

    public static final Logger LOG = LoggerFactory.getLogger(NodeFactoryImpl.class);

    /** The actor system. */
    @Autowired
    protected ActorSystem actorSystem;

    @Override
    public synchronized FlowElement createServiceNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {
        // event nodes
        if (flowNodeJaxb instanceof TStartEvent) {
            return this.createStartEventNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TIntermediateCatchEvent) {
            return this.createIntermediateCatchEventNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TIntermediateThrowEvent) {
            return this.createIntermediateThrowEventNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TEndEvent) {
            return this.createEndEventNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);

            // boundary event nodes
        } else if (flowNodeJaxb instanceof TBoundaryEvent) {
            return this.createIntermediateBoundaryEventNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);

            // gateways
        } else if (flowNodeJaxb instanceof TComplexGateway) {
            return this.createComplexGatewayNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TEventBasedGateway) {
            return this.createEventBasedGatewayNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TParallelGateway) {
            return this.createParallelGatewayNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TExclusiveGateway) {
            return this.createExclusiveGatewayNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);

            // activities
        } else if (flowNodeJaxb instanceof TReceiveTask) {
            return this.createReceiveTaskNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TSendTask) {
            return this.createSendTaskNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);
        } else if (flowNodeJaxb instanceof TServiceTask) {
            return this.createServiceTaskNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);

            // sub process nodes (without the included flow nodes)
        } else if (flowNodeJaxb instanceof TSubProcess) {
            return this.createSubProcessNode(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                    sequenceFlowsJaxb);

            // nodes that can not be matched
        } else {
            LOG.error(String.format("Unimplemented node >>>%s<<< catched while registering akka services",
                    flowNodeJaxb.getClass()));
            return null;
        }
    }

    /**
     * TODO --> comment
     * 
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the flow nodeJaxb
     * @param flowNodeJaxb the list of jaxb sequence flows of that process
     * @param sequenceFlowsJaxb
     * @return
     */
    private FlowElement createIntermediateBoundaryEventNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        TBoundaryEvent boundaryEvent = (TBoundaryEvent) flowNodeJaxb;

        return new IntermediateBoundaryEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, boundaryEvent),
                new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, boundaryEvent,
                        sequenceFlowsJaxb),
                this.getDataObjectHandling(flowNodeJaxb),
                this.getBoundaryActivity(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                boundaryEvent.isCancelActivity());
    }

    /**
     * Creates a start event node which can be used to create a start event
     * actor.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the start event node
     */
    private FlowElement createStartEventNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TStartEvent startEventJaxb = (TStartEvent) flowNodeJaxb;

        return new StartEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, startEventJaxb),
                new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, startEventJaxb,
                        sequenceFlowsJaxb),
                this.getOtherStartNodeActorReferences(clientId, processJaxb, subProcessesJaxb, startEventJaxb,
                        sequenceFlowsJaxb),
                this.getParentSubProcessUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                this.getDataObjectHandling(flowNodeJaxb));
    }

    /**
     * Creates a intermediate catch event node which can be used to create an
     * intermediate catch event actor.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the catch event node
     */
    private FlowElement createIntermediateCatchEventNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TCatchEvent intermediateCatchEventJaxb = (TCatchEvent) flowNodeJaxb;

        // check if catch event is following an event based gateway
        boolean ebgConnected = false;
        for (TFlowNode incomingNode : getIncomingFlowNodes(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                sequenceFlowsJaxb)) {
            if (incomingNode instanceof TEventBasedGateway) {
                ebgConnected = true;
                break;
            }
        }
        // if it is following an event based gateway instantiate this special
        // node
        if (ebgConnected) {
            return new EbgConnectedCatchEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                    IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb,
                            intermediateCatchEventJaxb),
                    new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                    this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb,
                            intermediateCatchEventJaxb, sequenceFlowsJaxb),
                    this.getDataObjectHandling(flowNodeJaxb));
            // else instantiate the standard catch node
        } else {
            return new IntermediateCatchEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                    IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb,
                            intermediateCatchEventJaxb),
                    new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                    this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb,
                            intermediateCatchEventJaxb, sequenceFlowsJaxb),
                    this.getDataObjectHandling(flowNodeJaxb));
        }
    }

    /**
     * Creates a intermediate throw event node which can be used to create an
     * intermediate throw event actor.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the throw event node
     */
    private FlowElement createIntermediateThrowEventNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TIntermediateThrowEvent intermediateThrowEventJaxb = (TIntermediateThrowEvent) flowNodeJaxb;

        return new IntermediateThrowEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, intermediateThrowEventJaxb),
                new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, intermediateThrowEventJaxb,
                        sequenceFlowsJaxb),
                this.getDataObjectHandling(flowNodeJaxb));
    }

    /**
     * Creates an end event node which can be used to create an end event actor.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the end event node
     */
    private FlowElement createEndEventNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        TEndEvent endEventJaxb = (TEndEvent) flowNodeJaxb;

        boolean isTerminate = checkIfTerminateEvent(endEventJaxb);

        return new EndEventNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, endEventJaxb),
                new EventDefinitionParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb), isTerminate,
                this.getParentSubProcessActorReference(clientId, processJaxb, subProcessesJaxb, endEventJaxb,
                        sequenceFlowsJaxb),
                this.getDataObjectHandling(flowNodeJaxb), this.getAllDataObjectIds(processJaxb, subProcessesJaxb));
    }

    private boolean checkIfTerminateEvent(TEndEvent endEventJaxb) {
        if (endEventJaxb.getEventDefinition().size() > 0) {
            if (endEventJaxb.getEventDefinition().get(0).getValue() instanceof TTerminateEventDefinition) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * Gets all data object ids of a process (including sub process object ids).
     *
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the list of parent jaxb sub processes
     * @return the data object ids
     */
    protected Set<String> getAllDataObjectIds(TProcess processJaxb, List<TSubProcess> subProcessesJaxb) {

        // only top level end processes need info about the data objects, 
        // because only they will be allowed to delete them
        if (subProcessesJaxb.size() == 0) {

            // create a set to only hold unique dataObjectIds (an objectId can be referenced from multiple flow nodes)
            Set<String> dataObjetIds = new HashSet<String>();

            dataObjetIds.addAll(DataObjectIdService.getAllDataObjectIds(processJaxb.getFlowElement()));

            dataObjetIds.remove(null);
            return dataObjetIds;
        }

        return null;
    }

    /**
     * Creates the complex gateway node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb complex gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the complex gateway node
     */
    private FlowElement createComplexGatewayNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TComplexGateway complexGatewayJaxb = (TComplexGateway) flowNodeJaxb;

        return new ComplexGatewayNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, complexGatewayJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, complexGatewayJaxb,
                        sequenceFlowsJaxb));
    }

    /**
     * Creates the event based gateway node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb event based gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the event based gateway node
     */
    private FlowElement createEventBasedGatewayNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TEventBasedGateway eventBasedGatewayJaxb = (TEventBasedGateway) flowNodeJaxb;

        return new EventBasedGatewayNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, eventBasedGatewayJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, eventBasedGatewayJaxb,
                        sequenceFlowsJaxb));
    }

    /**
     * Creates the parallel gateway node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the parallel gateway node
     */
    private FlowElement createParallelGatewayNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TParallelGateway parallelGatewayJaxb = (TParallelGateway) flowNodeJaxb;

        return new ParallelGatewayNode(IdService.getUniqueProcessId(clientId, processJaxb),
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, parallelGatewayJaxb),
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, parallelGatewayJaxb,
                        sequenceFlowsJaxb));
    }

    /**
     * Creates the exlusive fateway node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the list of parent jaxb sub processes
     * @param flowNodeJaxb the jaxb exclusive gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the flow element
     */
    private FlowElement createExclusiveGatewayNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        final TExclusiveGateway exclusiveGatewayJaxb = (TExclusiveGateway) flowNodeJaxb;

        //      this.getConditionalExpressionStrings(sequenceFlowsJaxb);

        return new ExclusiveGatewayNode(IdService.getUniqueProcessId(clientId, processJaxb), //process id
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, exclusiveGatewayJaxb), // node id
                this.getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, exclusiveGatewayJaxb,
                        sequenceFlowsJaxb), // outgoing node references
                this.getAllDataObjectIds(processJaxb, subProcessesJaxb), // the used data object ids
                this.getOutgoingActorReferencesAndExpressions(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                        sequenceFlowsJaxb), // all expressions including actor refs
                this.getDefaultOutgoingSequence(clientId, processJaxb, subProcessesJaxb, exclusiveGatewayJaxb), // the default actor ref
                this.getDataObjectHandling(flowNodeJaxb)); // the data object handler for the service
    }

    /**
     * Gives back the {@link ActorRef} to the default sequence flow. If no default
     * has been set, the the return value is null.
     *
     * @param clientId the client id
     * @param processJaxb the process jaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param exclusiveGatewayJaxb the exclusive gateway jaxb
     * @return the default outgoing sequence
     */
    private ActorRef getDefaultOutgoingSequence(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TExclusiveGateway exclusiveGatewayJaxb) {

        Object def = exclusiveGatewayJaxb.getDefault();

        if (def != null) {
            TSequenceFlow sequenceFlowJaxb = (TSequenceFlow) def;
            // actor reference
            return new ActorReferenceService().getActorReference(IdService.getUniqueFlowNodeId(clientId,
                    processJaxb, subProcessesJaxb, (TFlowNode) sequenceFlowJaxb.getTargetRef()));
        }
        return null;
    }

    /**
     * get conditional expression
     * 
     * @param sequenceFlow
     * @return
     */
    private String getConditionalExpressionString(TSequenceFlow sequenceFlow) {
        TExpression expression = sequenceFlow.getConditionExpression();
        if (expression != null) {
            // we assume, that there's only one expression per node
            return (String) expression.getContent().get(0);
        }

        return null;
    }

    /**
     * Creates a new sub process node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the sub process node
     */
    protected FlowElement createSubProcessNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {
        return getLoopTaskWrapper(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb, sequenceFlowsJaxb);
    }

    /**
     * Creates the send task node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the send task node
     */
    protected FlowElement createSendTaskNode(final String clientId, final TProcess processJaxb,
            final List<TSubProcess> subProcessesJaxb, final TFlowNode flowNodeJaxb,
            final List<TSequenceFlow> sequenceFlowsJaxb) {
        return getLoopTaskWrapper(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb, sequenceFlowsJaxb);
    }

    /**
     * Gets the loop task wrapper which isolates the looping behavior from the task action. 
     * It therefore creates and calls the according {@link LoopStrategy} which itself creates and calls
     * the task action.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the loop task wrapper
     */
    private FlowElement getLoopTaskWrapper(final String clientId, final TProcess processJaxb,
            final List<TSubProcess> subProcessesJaxb, final TFlowNode flowNodeJaxb,
            final List<TSequenceFlow> sequenceFlowsJaxb) {
        final NodeParameter nodeParameter = new NodeParameter(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                sequenceFlowsJaxb);
        final String uniqueFlowNodeId = nodeParameter.getUniqueFlowNodeId();

        // create the loop strategy actor which itself creates the task action actor
        ActorRef strategy = this.createLoopStrategy(flowNodeJaxb, nodeParameter, uniqueFlowNodeId);

        // return the LoopTaskWrapper which holds the LoopTypeStrategy
        return new ActivityLoopWrapper(nodeParameter.getUniqueProcessId(), uniqueFlowNodeId,
                getOutgoingActorReferences(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb,
                        sequenceFlowsJaxb),
                strategy, getBoundaryEvents(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb));
    }

    /**
     * Creates the loop strategy depending on the loop configuration of a node.
     *
     * @param flowNodeJaxb the jaxb flow node 
     * @param nodeParameter the node parameter
     * @param uniqueFlowNodeId the unique flow node id
     * @return the LoopStrategy actor ref
     */
    private ActorRef createLoopStrategy(final TFlowNode flowNodeJaxb, final NodeParameter nodeParameter,
            final String uniqueFlowNodeId) {
        ActorRef strategy = this.actorSystem.actorOf(new Props(new UntypedActorFactory() {
            private static final long serialVersionUID = 1L;

            public UntypedActor create() {
                return new LoopStrategyFactory().createLoopStrategy(
                        new ActorReferenceService().getActorReference(uniqueFlowNodeId), // task wrapper actor Ref
                        nodeParameter);
            }
        }).withDispatcher("file-mailbox-dispatcher"),
                ActorReferenceService.getActorReferenceString(uniqueFlowNodeId) + "-strategy");

        LOG.debug(String.format("%s --> resulting akka object: %s", flowNodeJaxb, strategy.toString()));
        return strategy;
    }

    /**
     * Gets the activity a boundary event is connected to.
     *
     * @param clientId the client id
     * @param processJaxb the process jaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb flow node
     * @return the boundary activity
     */
    private ActorRef getBoundaryActivity(String clientId, TProcess processJaxb, List<TSubProcess> subProcessesJaxb,
            TFlowNode flowNodeJaxb) {

        TBoundaryEvent boundaryEvent = (TBoundaryEvent) flowNodeJaxb;

        String connectedTo = boundaryEvent.getAttachedToRef().getLocalPart();
        TFlowNode connectedToFlowNodeJaxb = IdService.getTFlowNodeById(processJaxb, connectedTo);

        return new ActorReferenceService().getActorReference(
                IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, connectedToFlowNodeJaxb));
    }

    /**
     * Gets the activity a boundary event is connected to.
     *
     * @param clientId the client id
     * @param processJaxb the process jaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb flow node
     * @return the boundary activity
     */
    protected List<ActorRef> getBoundaryEvents(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb) {

        List<TFlowNode> boundaryEventsJaxb = this.getTBoundaryEventById(processJaxb, flowNodeJaxb.getId());

        if (boundaryEventsJaxb == null) {
            return null;
        } else {
            List<ActorRef> boundaryEvents = new ArrayList<ActorRef>();
            for (TFlowNode boundaryEvent : boundaryEventsJaxb) {
                boundaryEvents.add(new ActorReferenceService().getActorReference(
                        IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb, boundaryEvent)));
            }
            return boundaryEvents;
        }
    }

    /**
     * Gets the boundary event nodes by the id of the node connected to. The information whether an activity binds a boundary event
     * or not is only available at the TBoundaryEvent. The TActivity does not have this information so we have to parse the 
     * TProcess. 
     *
     * @param processJaxb the jaxb process
     * @param activityNodeId the node id of the element the boundary event is connected to 
     * @return the boundary node that is attached
     */
    private List<TFlowNode> getTBoundaryEventById(TProcess processJaxb, String activityNodeId) {

        List<TFlowNode> flowNodesJaxb = new ArrayList<TFlowNode>();

        for (JAXBElement<? extends TFlowElement> flowElementJaxb : processJaxb.getFlowElement()) {
            if (flowElementJaxb.getValue() instanceof TBoundaryEvent) {

                TBoundaryEvent boundaryEvent = (TBoundaryEvent) flowElementJaxb.getValue();

                if (boundaryEvent.getAttachedToRef().getLocalPart().equals(activityNodeId)) {
                    LOG.debug(String.format("Found Boundary Event Node with id ", boundaryEvent.getId()));
                    flowNodesJaxb.add(boundaryEvent);
                }
            } else if (flowElementJaxb.getValue() instanceof TSubProcess) {
                flowNodesJaxb.addAll(getTBoundaryEventByIdFromSubprocess((TSubProcess) flowElementJaxb.getValue(),
                        activityNodeId));
            }
        }

        if (flowNodesJaxb.size() == 0) {
            LOG.debug("There are no boundary events for node id " + activityNodeId);
            return null;
        } else {
            return flowNodesJaxb;
        }
    }

    /**
     * Gets the TBoundaryEvents by id from subprocesses (recursively).
     *
     * @param subProcessJaxb the jaxb sub process 
     * @param activityNodeId the activity node id
     * @return the TBoundaryEvents from subprocesses
     */
    private List<TFlowNode> getTBoundaryEventByIdFromSubprocess(TSubProcess subProcessJaxb, String activityNodeId) {

        List<TFlowNode> boundaryEventsJaxb = new ArrayList<TFlowNode>();

        for (JAXBElement<? extends TFlowElement> flowElementJaxb : subProcessJaxb.getFlowElement()) {
            if (flowElementJaxb.getValue() instanceof TBoundaryEvent) {

                TBoundaryEvent boundaryEvent = (TBoundaryEvent) flowElementJaxb.getValue();

                if (boundaryEvent.getAttachedToRef().getLocalPart().equals(activityNodeId)) {
                    LOG.debug(String.format("Found Boundary Event Node with id ", boundaryEvent.getId()));
                    boundaryEventsJaxb.add(boundaryEvent);
                }
            } else if (flowElementJaxb.getValue() instanceof TSubProcess) {
                boundaryEventsJaxb.addAll(getTBoundaryEventByIdFromSubprocess(
                        (TSubProcess) flowElementJaxb.getValue(), activityNodeId));
            }
        }

        return boundaryEventsJaxb;
    }

    /**
     * Creates the receive task node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the send task node
     */
    protected FlowElement createReceiveTaskNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {
        return getLoopTaskWrapper(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb, sequenceFlowsJaxb);
    }

    /**
     * Creates the supervising service task node.
     *
     * @param clientId the client id
     * @param processJaxb the jaxb process
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the jaxb parallel gateway node
     * @param sequenceFlowsJaxb the list of jaxb sequence flows of that process
     * @return the service task node
     */
    protected FlowElement createServiceTaskNode(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {
        return getLoopTaskWrapper(clientId, processJaxb, subProcessesJaxb, flowNodeJaxb, sequenceFlowsJaxb);
    }

    /**
     * Extracts the information which nodes are incoming to a given node and
     * returns the actor references to that nodes. This is needed, because
     * sequence flows are not modeled as actors at the moment (because there is
     * no need for them).
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return a list of strings of the incoming actor references
     */
    private List<TFlowNode> getIncomingFlowNodes(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        List<TFlowNode> incomingingNodes = new ArrayList<TFlowNode>();

        // check the sequence flows if they connect the given flow node to any
        // other flow node
        for (TSequenceFlow sequenceFlowJaxb : sequenceFlowsJaxb) {
            /*
             * if the provided flow node equals the target of a sequence flow,
             * add it to the incoming nodes list as a actor reference string
             * (the actor reference object might not be initialized at this
             * point)
             */
            if (sequenceFlowJaxb.getTargetRef().equals(flowNodeJaxb)) {
                /*
                 * bpmn allows flow nodes to be connected to other objects (eg.
                 * messages) we do not want to connect these as they are
                 * handeled by other components of the engine
                 */
                if (sequenceFlowJaxb.getSourceRef() instanceof TFlowNode) {
                    incomingingNodes.add((TFlowNode) sequenceFlowJaxb.getSourceRef());
                }
            }
        }
        return incomingingNodes;
    }

    /**
     * Extracts the information which nodes follow to a given node and returns
     * the actor references to that nodes. This is needed, because sequence
     * flows are not modeled as actors at the moment (because there is no need
     * for them).
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return a list of strings of the outgoing actor references
     */
    protected List<ActorRef> getOutgoingActorReferences(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        List<ActorRef> outgoingNodes = new ArrayList<ActorRef>();
        // build the actorRef and expression pairs
        Map<ActorRef, String> outgoingActorReferencesAndExpressions = this.getOutgoingActorReferencesAndExpressions(
                clientId, processJaxb, subProcessesJaxb, flowNodeJaxb, sequenceFlowsJaxb);
        // we'll only need the keys (actor refs)
        outgoingNodes.addAll(outgoingActorReferencesAndExpressions.keySet());
        return outgoingNodes;
    }

    /**
     * Gets the outgoing actor references and expressions.
     *
     * @param clientId the client id
     * @param processJaxb the process jaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow node jaxb
     * @param sequenceFlowsJaxb the sequence flows jaxb
     * @return the outgoing actor references and expressions
     */
    private Map<ActorRef, String> getOutgoingActorReferencesAndExpressions(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {
        Map<ActorRef, String> outRefs = new TreeMap<ActorRef, String>();

        // check the sequence flows if they connect the given flow node to any
        // other flow node
        for (TSequenceFlow sequenceFlowJaxb : sequenceFlowsJaxb) {
            /*
             * if the provided flow node equals the source of a sequence flow,
             * add it to the outgoing nodes list as a actor reference string
             * (the actor reference object might not be initialized at this
             * point)
             */
            if (sequenceFlowJaxb.getSourceRef().equals(flowNodeJaxb)) {
                /*
                 * bpmn allows flow nodes to be connected to other objects (eg.
                 * messages) we do not want to connect these as they are
                 * handeled by other components of the engine
                 */
                if (sequenceFlowJaxb.getTargetRef() instanceof TFlowNode) {

                    // actor reference
                    ActorRef actorRef = new ActorReferenceService().getActorReference(IdService.getUniqueFlowNodeId(
                            clientId, processJaxb, subProcessesJaxb, (TFlowNode) sequenceFlowJaxb.getTargetRef()));

                    // conditional expression
                    String expressionString = this.getConditionalExpressionString(sequenceFlowJaxb);

                    outRefs.put(actorRef, expressionString);
                }

            }
        }
        return outRefs;
    }

    /**
     * Extracts the information of the embedding/parent sub process node's ActorRef. Used for Start and End Events which are logically connected to their
     * embedding sub process nodes.
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return the ActorRef of the parent Sub Process or null if there is none
     */
    private ActorRef getParentSubProcessActorReference(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        String subProcessUinqueFlowNodeId = getParentSubProcessUniqueFlowNodeId(clientId, processJaxb,
                subProcessesJaxb, flowNodeJaxb);

        if (subProcessUinqueFlowNodeId != null) {
            return new ActorReferenceService().getActorReference(subProcessUinqueFlowNodeId);
        } else {
            return null;
        }
    }

    /**
     * Extracts the information of the embedding/parent sub process node's uniqueFlowNodeId. Used for Start and End Events which are logically connected to their
     * embedding sub process nodes.
     *
     * @param clientId the client id
     * @param processJaxb the process jaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow node jaxb
     * @return the unique flow node id of the parent sub process or null if there is none
     */
    private String getParentSubProcessUniqueFlowNodeId(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb) {
        ArrayList<TSubProcess> localSubProcessesJaxb = new ArrayList<TSubProcess>(subProcessesJaxb);

        // check if the current end event node is a embedded in a sub process
        if (localSubProcessesJaxb.size() > 0) {
            // we only check the last sub process, because this can only be the sub process we are looking for
            TSubProcess subProcessJaxb = localSubProcessesJaxb.get(localSubProcessesJaxb.size() - 1);
            for (JAXBElement<? extends TFlowElement> flowElementJaxb : subProcessJaxb.getFlowElement()) {
                // if the given end event node has been found in this sub process this is an embedded end event 
                if (flowElementJaxb.getValue().getId().equals(flowNodeJaxb.getId())) {
                    // remove the current sub process from the list of sub processes to be able to get its actor reference
                    localSubProcessesJaxb.remove(subProcessJaxb);
                    return IdService.getUniqueFlowNodeId(clientId, processJaxb, localSubProcessesJaxb,
                            subProcessJaxb);
                }
            }
        }

        // return null if there is no embedding/parent sub process
        return null;
    }

    /**
     * Extracts the information which other start nodes are in a given process
     * and returns the actor references to that nodes. This info is needed for
     * process instantiation.
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return a list of strings of the other start node actor references
     */
    private List<ActorRef> getOtherStartNodeActorReferences(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        List<ActorRef> otherStartNodes = new ArrayList<ActorRef>();

        // check the sequence flows for if there are any other start nodes in
        // this process
        for (TSequenceFlow sequenceFlowJaxb : sequenceFlowsJaxb) {

            // if this is a start event...
            if ((sequenceFlowJaxb.getSourceRef().getClass().equals(TStartEvent.class))
                    // ... but not the start event, that is initialized ...
                    && (!((TFlowNode) sequenceFlowJaxb.getSourceRef()).getId().equals(flowNodeJaxb.getId()))) {

                /*
                 * ...add it to the outgoing nodes list as an actor reference
                 * string. (the actor reference object might not be initialized
                 * at this point)
                 */
                otherStartNodes
                        .add(new ActorReferenceService().getActorReference(IdService.getUniqueFlowNodeId(clientId,
                                processJaxb, subProcessesJaxb, (TFlowNode) sequenceFlowJaxb.getSourceRef())));

            }
        }
        return otherStartNodes;
    }

    /**
     * Extracts the information of the embedded start nodes that are in a given process (level)
     * and returns the actor references to that nodes. This info is needed by the sub process.
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb (which must be a TSubProcess)
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return a list of strings of the other start node actor references
     */
    protected List<ActorRef> getEmbeddedStartNodeActorReferences(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        List<ActorRef> embeddedStartNodes = new ArrayList<ActorRef>();

        // the current sub process is the parent process of the embedded nodes, 
        // so we need to add it to the list of parent sub processes in this methods scope
        ArrayList<TSubProcess> subProcesses = new ArrayList<TSubProcess>(subProcessesJaxb);
        subProcesses.add((TSubProcess) flowNodeJaxb);

        // extract StartEvents from SubProcess
        for (JAXBElement<? extends TFlowElement> flowElementJaxb : ((TSubProcess) flowNodeJaxb).getFlowElement()) {
            if (flowElementJaxb.getValue() instanceof TStartEvent) {
                TStartEvent startEventJaxb = (TStartEvent) flowElementJaxb.getValue();
                embeddedStartNodes.add(new ActorReferenceService().getActorReference(
                        IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcesses, startEventJaxb)));
            }
        }

        return embeddedStartNodes;
    }

    /**
     * Extracts the information of the embedded start nodes that are in a given process (level)
     * and returns the actor references to that nodes. This info is needed by the sub process.
     *
     * @param clientId the client id
     * @param processJaxb the processJaxb
     * @param subProcessesJaxb the sub processes jaxb
     * @param flowNodeJaxb the flow nodeJaxb
     * @param sequenceFlowsJaxb the sequence flowsJaxb
     * @return a list of strings of the other start node actor references
     */
    protected List<ActorRef> getEmbeddedNodeActorReferences(String clientId, TProcess processJaxb,
            List<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb, List<TSequenceFlow> sequenceFlowsJaxb) {

        if (flowNodeJaxb instanceof TSubProcess) {
            TSubProcess subProcessJaxb = (TSubProcess) flowNodeJaxb;

            // the searched embedded flow nodes are embedded in teh given subprocess (so we need to add it to the list of (embedding) subprocesses)
            ArrayList<TSubProcess> embeddingSubProcessesJaxb = new ArrayList<TSubProcess>(subProcessesJaxb);
            embeddingSubProcessesJaxb.add(subProcessJaxb);

            // collect actorRef for each flow node in the subProcess
            List<ActorRef> embeddedFlowNodes = new ArrayList<ActorRef>();

            for (JAXBElement<? extends TFlowElement> flowElementJaxb : subProcessJaxb.getFlowElement()) {
                if (flowElementJaxb.getValue() instanceof TFlowNode) {
                    TFlowNode embeddedFlowNodeJaxb = (TFlowNode) flowElementJaxb.getValue();
                    embeddedFlowNodes.add(
                            new ActorReferenceService().getActorReference(IdService.getUniqueFlowNodeId(clientId,
                                    processJaxb, embeddingSubProcessesJaxb, embeddedFlowNodeJaxb)));
                }
            }

            return embeddedFlowNodes;
        } else {
            return null;
        }
    }

    public DataObjectHandling getDataObjectHandling(TFlowNode flowNodeJaxb) {

        Map<String, String> dataObjectIds = DataObjectIdService.getDataObjectIds(flowNodeJaxb);

        // only create a DataObjectService with a data object service provider if there is at least on object id
        if (dataObjectIds.size() > 0) {
            LOG.debug("Node factory appending data object service provider");
            return new DataObjectHandling(dataObjectIds.get(DataObjectIdService.DATAINPUTOBJECTID),
                    dataObjectIds.get(DataObjectIdService.DATAOUTPUTOBJECTID));
        }
        // otherwise provide a dummy DataObjectService
        else {
            return new DataObjectHandling(new NoDataObjectSP());
        }
    }

    // currently unsused methods (left if needed later)

    //   /**
    //    * Extracts the information which nodes are incoming to a given node and
    //    * returns the actor references to that nodes. This is needed, because
    //    * sequence flows are not modeled as actors at the moment (because there is
    //    * no need for them).
    //    *
    //    * @param clientId the client id
    //    * @param processJaxb the processJaxb
    //    * @param flowNodeJaxb the flow nodeJaxb
    //    * @param sequenceFlowsJaxb the sequence flowsJaxb
    //    * @return a list of strings of the incoming actor references
    //    */
    //   private List<ActorRef> getIncomingActorReferences(
    //         String clientId, TProcess processJaxb, ArrayList<TSubProcess> subProcessesJaxb, TFlowNode flowNodeJaxb,
    //         List<TSequenceFlow> sequenceFlowsJaxb) {
    //
    //      List<ActorRef> incomingingNodes = new ArrayList<ActorRef>();
    //
    //      // check the sequence flows if they connect the given flow node to any
    //      // other flow node
    //      for (TSequenceFlow sequenceFlowJaxb : sequenceFlowsJaxb) {
    //         /*
    //          * if the provided flow node equals the target of a sequence flow,
    //          * add it to the incoming nodes list as a actor reference string
    //          * (the actor reference object might not be initialized at this
    //          * point)
    //          */
    //         if (sequenceFlowJaxb.getTargetRef().equals(flowNodeJaxb)) {
    //            /*
    //             * bpmn allows flow nodes to be connected to other objects (eg.
    //             * messages) we do not want to connect these as they are
    //             * handeled by other components of the engine
    //             */
    //            if (sequenceFlowJaxb.getSourceRef() instanceof TFlowNode) {
    //               incomingingNodes.add(new ActorReferenceService()
    //                     .getActorReference(
    //                           IdService.getUniqueFlowNodeId(clientId, processJaxb, subProcessesJaxb,
    //                                 (TFlowNode) sequenceFlowJaxb.getSourceRef())));
    //            }
    //         }
    //      }
    //      return incomingingNodes;
    //   }
}