Package de.danet.an.workflow.clients.wfxml

Source Code of de.danet.an.workflow.clients.wfxml.InstanceResponseGenerator

/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2006 Danet GmbH (www.danet.de), BU BTS.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* $Id: InstanceResponseGenerator.java 2331 2007-03-29 11:46:54Z schnelle $
*
* $Log$
* Revision 1.11  2007/03/27 21:59:42  mlipp
* Fixed lots of checkstyle warnings.
*
* Revision 1.10  2007/03/01 12:32:57  schnelle
* Enhanced Instance.SetProperties to process ContextData.
*
* Revision 1.9  2007/02/16 21:00:21  mlipp
* Fixed some null pointer problems.
*
* Revision 1.8  2007/02/01 13:44:43  schnelle
* Using namespace for factory schemas that do not contain '&'.
*
* Revision 1.7  2007/02/01 12:38:36  schnelle
* Use of encoded key for properties.
*
* Revision 1.6  2007/01/31 22:55:36  mlipp
* Some more refactoring and fixes of problems introduced by refactoring.
*
* Revision 1.5  2007/01/31 14:53:06  schnelle
* Small corrections wvaluating the resource reference.
*
* Revision 1.4  2007/01/31 12:24:06  drmlipp
* Design revisited.
*
* Revision 1.3  2007/01/30 15:07:37  schnelle
* Filtering of all instances for the current factory in a list instances request.
*
* Revision 1.2  2007/01/30 11:56:14  drmlipp
* Merged Wf-XML branch.
*
* Revision 1.1.2.23  2007/01/29 15:04:19  schnelle
* Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
*
* Revision 1.1.2.22  2007/01/29 13:40:30  schnelle
* Storing of the sender base in the servlet context.
*
* Revision 1.1.2.21  2007/01/29 13:10:27  drmlipp
* Using utilities method for timestamp convertion.
*
* Revision 1.1.2.20  2007/01/24 11:46:35  schnelle
* Moved wsdl files and xsd files intot resources subdirectory.
*
* Revision 1.1.2.19  2007/01/24 10:56:50  schnelle
* Prepared return of a result for aobservers.
*
* Revision 1.1.2.18  2007/01/24 09:28:53  schnelle
* Returning result data for the instance.
*
* Revision 1.1.2.17  2007/01/19 15:01:59  schnelle
* Returning context data at instance getproperties.
*
* Revision 1.1.2.16  2007/01/19 12:34:56  schnelle
* Moved generation and decoding of the URI that is used as the receiver key to new class URIDecoder.
*
* Revision 1.1.2.15  2007/01/16 11:05:42  schnelle
* Refactoring: Moved subscription handling methods to own class.
*
* Revision 1.1.2.14  2007/01/11 11:37:10  schnelle
* Added subscription if an oberver key is given in the creation of a process.
*
* Revision 1.1.2.13  2007/01/10 14:48:18  schnelle
* Checked if a subscription already exists, before storing the observer in the database.
*
* Revision 1.1.2.12  2007/01/10 14:16:52  schnelle
* Implemented unsubscribe.
*
* Revision 1.1.2.11  2007/01/10 13:41:27  schnelle
* Implemented subscribe.
*
* Revision 1.1.2.10  2007/01/10 09:03:43  schnelle
* Implemented set properties methods.
*
* Revision 1.1.2.9  2006/12/20 13:32:24  schnelle
* Basic implementato of GetProperties for Instance and Activity.
*
* Revision 1.1.2.8  2006/12/18 14:41:02  schnelle
* Preparatation for individual schema definition for each getproperties request.
*
* Revision 1.1.2.7  2006/12/14 08:50:21  schnelle
* Implemented CompleteActivity.
*
* Revision 1.1.2.6  2006/12/13 11:23:48  schnelle
* Implemented instance ListActivities.
*
* Revision 1.1.2.5  2006/12/12 13:24:38  schnelle
* Introduction of ASAPException to provide a detailed mesage.
*
* Revision 1.1.2.4  2006/12/12 11:41:21  schnelle
* Implemented state mapping of omg states to asap states.
*
* Revision 1.1.2.3  2006/12/12 09:34:35  schnelle
* Implemented ChangeState for Instance.
*
* Revision 1.1.2.2  2006/12/11 11:05:34  schnelle
* Added template methods for all requests.
*
* Revision 1.1.2.1  2006/11/28 12:20:10  schnelle
* Creation of a separate class to handle the issues for a specific resource.
*
* Revision 1.1.2.2  2006/11/27 15:41:55  schnelle
* Introducing some constants for request and response identification.
*
* Revision 1.1.2.1  2006/11/24 12:19:13  schnelle
* Separtion of response generation into ResponseGenerator class.
*
*/
package de.danet.an.workflow.clients.wfxml;

import java.rmi.RemoteException;
import java.sql.SQLException;
import java.text.ParseException;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.NoSuchElementException;

import javax.xml.soap.MessageFactory;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.TransformerException;

import org.xml.sax.SAXException;

import de.danet.an.util.XMLUtil;
import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.DefaultProcessData;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.Process;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.ProcessDirectory;
import de.danet.an.workflow.api.SAXEventBuffer;
import de.danet.an.workflow.api.WorkflowService;
import de.danet.an.workflow.assignment.Assignment;
import de.danet.an.workflow.clients.wfxml.ObserverRegistry.ObserverInfo;
import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
import de.danet.an.workflow.omgcore.InvalidDataException;
import de.danet.an.workflow.omgcore.InvalidStateException;
import de.danet.an.workflow.omgcore.ProcessData;
import de.danet.an.workflow.omgcore.ProcessDataInfo;
import de.danet.an.workflow.omgcore.ResultNotAvailableException;
import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
import de.danet.an.workflow.omgcore.WfAuditEvent;
import de.danet.an.workflow.omgcore.WfCreateProcessAuditEvent;
import de.danet.an.workflow.omgcore.WfProcess;
import de.danet.an.workflow.omgcore.WfResource;
import de.danet.an.workflow.omgcore.WfStateAuditEvent;

/**
* This class provides the methods of an {@link AbstractResponseGenerator}
* that are relevant for the instance.
*
* <p>
* The <em>Instance</em> resource is the actual "performance of work"; it is the
* process instance. It embodies the context information that distinguishes one
* process instance from another. Some people call this a "case". A process
* instance resource can be used only once: it is created, then it can be
* started, it can be paused, resumed, terminated. If things go normally, it
* will eventually complete.
* </p>
*
* @author Dirk Schnelle
*
*/
class InstanceResponseGenerator extends AbstractResponseGenerator {
    /** Logger instance. */
    private static final org.apache.commons.logging.Log logger
        = org.apache.commons.logging.LogFactory.getLog
                (InstanceResponseGenerator.class);   

    /**
     * Constructs a new object.
     *
     * @param observerRegistry the observer registry.
     * @param wfs Reference to the workflow engine.
     * @param resRef the resource reference.
     */
    public InstanceResponseGenerator
        (ObserverRegistry observerRegistry,
         WorkflowService wfs, ResourceReference resRef){
        super(observerRegistry, wfs, resRef);
    }
   
    /* (non-Javadoc)
     * @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator
     */
    public void evaluate(SOAPMessage reqMsg, SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        SOAPBodyElement actionElement = getActionElement(reqMsg);
       
        String actName = actionElement.getElementName().getLocalName();

        if (actName.equals (Consts.GET_PROPERTIES_REQUEST)) {
            getInstanceProperties(reqMsg, respMsg);
        } else if (actName.equals (Consts.SET_PROPERTIES_REQUEST)) {
            setInstanceProperties(actionElement, reqMsg, respMsg);
        } else if (actName.equals (Consts.SUBSCRIBE_REQUEST)) {
            subscribe(actionElement, reqMsg, respMsg);
        } else if (actName.equals (Consts.UNSUBSCRIBE_REQUEST)) {
            unsubscribe(actionElement, reqMsg, respMsg);
        } else if (actName.equals (Consts.LIST_ACTIVITIES_REQUEST)) {
            listActivities(reqMsg, respMsg);
        } else if (actName.equals (Consts.CHANGE_STATE_REQUEST)) {
            changeState(actionElement, reqMsg, respMsg);
        } else if (actName.equals (Consts.TERMINATE_REQUEST)) {
            terminate(reqMsg, respMsg);
        } else {
            if (logger.isDebugEnabled()) {
                logger.debug("unknown action '" + actName + "'");
            }

            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_OPERATION_SPECIFICATION,
                    getResourceName() + ": Unknown action \"" + actName
                    + "\".");
        }
    }

    /* (non-Javadoc)
     * @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator#getSender()
     */
    protected String getResourceName() {
        return RESOURCE_INSTANCE;
    }

    /**
     * Creates a response that contains properties of the process instance.
     *
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void getInstanceProperties(SOAPMessage reqMsg, SOAPMessage respMsg)
            throws SOAPException, RemoteException {
        if (logger.isDebugEnabled()) {
            logger.debug("get instance properties...");
        }
       
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        Process proc = null;
        try {
            proc = (Process) getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        }
    
        SOAPBodyElement propsNode
            = createAsapResponseNode(respMsg, Consts.GET_PROPERTIES_RESPONSE);
       
        try {
            appendInstanceProperties(respMsg, receiverKey, proc, propsNode);
        } catch (ResultNotAvailableException e) {
            FaultUtils.setFault(respMsg, e);
           
            return;
        }
    }

    /**
     * Reads the properties from the process instance and appends them to the
     * parent.
     * @param respMsg the response message.
     * @param receiverKey this activity instance
     * @param parent the parent node of the response message.
     * @param proc the process
     * @throws SOAPException
     *         error appending to the parent node
     * @throws RemoteException
     *         error accessing the process
     * @throws SAXException
     * @throws TransformerException
     * @throws ResultNotAvailableException
     */
    private void appendInstanceProperties(SOAPMessage respMsg,
            String receiverKey, Process proc, SOAPBodyElement propsNode)
            throws SOAPException, RemoteException, ResultNotAvailableException {
        SOAPElement keyNode
            = propsNode.addChildElement("Key", Consts.ASAP_PREFIX);
        keyNode.addTextNode (getResourceReference().getResourceKey());
        SOAPElement state
            = propsNode.addChildElement("State", Consts.ASAP_PREFIX);
        String procState = proc.state();
        state.addTextNode(StateMapper.omg2asapState(procState));
        SOAPElement nameNode
            = propsNode.addChildElement("Name", Consts.ASAP_PREFIX);
        nameNode.addTextNode(proc.name());
        SOAPElement subjectNode
            = propsNode.addChildElement("Subject", Consts.ASAP_PREFIX);
        if (proc.description() != null) {
            subjectNode.addTextNode(proc.description());
        }
        SOAPElement descNode
            = propsNode.addChildElement("Description", Consts.ASAP_PREFIX);
        if (proc.description() != null) {
            descNode.addTextNode(proc.description());
        }
        SOAPElement factoryKey
            = propsNode.addChildElement("FactoryKey", Consts.ASAP_PREFIX);
        ResourceReference procDefRes = new ResourceReference
            (getResourceReference().getBaseUrl(), proc.processDefinition());
        factoryKey.addTextNode(procDefRes.getResourceKey());
       
        appendObservers(respMsg, proc, propsNode);
        appendHistory(respMsg, proc, propsNode);
        appendContextData(respMsg, proc, propsNode);
        appendResultData(respMsg, proc, propsNode);
    }

    /**
     * Appends the list of observers to the given node in a properties request.
     * @param respMsg the message to send
     * @param proc the related process
     * @param propsNode the parent node
     * @throws RemoteException
     *         Error accessing the workflow engine
     * @throws SOAPException
     *         Error creating the message
     */
    private void appendObservers(SOAPMessage respMsg, Process proc,
            SOAPBodyElement propsNode) throws RemoteException, SOAPException {
        Collection observers;
        try {
            ObserverRegistry obs = getObserverRegistry();
            ProcessDefinition procdef = proc.processDefinition();
            observers = obs.getObservers(procdef.packageId(),
                    procdef.processId(), proc.key());
        } catch (SQLException e) {
            FaultUtils.setFault(respMsg, e);
            return;
        }
       
        if (observers.size() > 0) {
            SOAPElement observersNode
                = propsNode.addChildElement("Observers", Consts.ASAP_PREFIX);
            Iterator iterator = observers.iterator();
            while (iterator.hasNext()) {
                ObserverInfo observerInfo = (ObserverInfo) iterator.next();
                SOAPElement observerKey
                    = observersNode.addChildElement("ObserverKey",
                        Consts.ASAP_PREFIX);
                observerKey.addTextNode(observerInfo.getObserverKey());

            }
        }
    }

    /**
     * Appends the history of the process to the given node in a properties
     * request.
     * @param respMsg the message to send
     * @param proc the related process
     * @param propsNode the parent node
     * @throws RemoteException
     *         Error accessing the workflow engine
     * @throws SOAPException
     *         Error creating the message
     */
    private void appendHistory(SOAPMessage respMsg, Process proc,
            SOAPBodyElement propsNode) throws RemoteException, SOAPException {
        SOAPElement historyNode
            = propsNode.addChildElement("History", Consts.ASAP_PREFIX);

        Collection history;
        try {
            history = proc.history();
        } catch (HistoryNotAvailableException e) {
            // well, if there is nothing to append...
            return;
        }
        Iterator iterator = history.iterator();
        while (iterator.hasNext()) {
            WfAuditEvent event = (WfAuditEvent) iterator.next();

            String asapEventType = getAsapEventType(event);
            if (asapEventType == null) {
                continue;
            }
           
            SOAPElement eventNode
                = historyNode.addChildElement("Event", Consts.ASAP_PREFIX);
            Date date = event.timeStamp();
            SOAPElement timeNode
                = eventNode.addChildElement("Time", Consts.ASAP_PREFIX);
            timeNode.addTextNode(XMLUtil.toXsdGMTDateTime(date));
            SOAPElement type
                = eventNode.addChildElement("EventType", Consts.ASAP_PREFIX);
            type.addTextNode(asapEventType);
            SOAPElement sourceKey
                = eventNode.addChildElement("SourceKey", Consts.ASAP_PREFIX);
            sourceKey.addTextNode("");
            SOAPElement details
                = eventNode.addChildElement("Details", Consts.ASAP_PREFIX);
            details.addTextNode("");
            String newState;
            String oldState;
           
            if (event instanceof WfCreateProcessAuditEvent) {
                // The old state is not defined, and we are not allowed to
                // add an empty string.
                oldState = "open.notrunning";
                newState = "open.notrunning";
            } else {
                WfStateAuditEvent stateEvent = (WfStateAuditEvent) event;
                oldState = StateMapper.omg2asapState(stateEvent.oldState());
                newState = StateMapper.omg2asapState(stateEvent.newState());
            }
            SOAPElement oldStateNode
                = eventNode.addChildElement("OldState", Consts.ASAP_PREFIX);
            oldStateNode.addTextNode(oldState);
            SOAPElement newStateNode
                = eventNode.addChildElement("NewState", Consts.ASAP_PREFIX);
            newStateNode.addTextNode(newState);
           
        }
    }

    /**
     * Gets the ASAP description for the {@link WfAuditEvent}.
     * @param event the event
     * @return ASAP representation of the event or <code>null</null>
     * if no corresponding ASAP event exists..
     */
    private String getAsapEventType(WfAuditEvent event)  {
        String type = event.eventType();
        if (type.equals(WfAuditEvent.PROCESS_CREATED)) {
            return "InstanceCreated";
        }

        if (type.equals(WfAuditEvent.PROCESS_STATE_CHANGED)) {
            return "StateChanged";
        }
        return null;
    }
   
    /**
     * Appends the context data to the given node in a properties request.
     * @param respMsg the message to send
     * @param proc the related process
     * @param propsNode the parent node
     * @throws RemoteException
     *         Error accessing the workflow engine
     * @throws SOAPException
     *         Error creating the message
     * @throws SAXException
     * @throws TransformerException
     */
    private void appendContextData(SOAPMessage respMsg, Process proc,
            SOAPBodyElement propsNode)
        throws RemoteException, SOAPException {
        SOAPElement contextData
            = propsNode.addChildElement("ContextData", Consts.ASAP_PREFIX);
               
        contextData.addNamespaceDeclaration
            (Consts.CD_PREFIX, getResourceReference().getNamespace());
       
        ProcessData context = proc.processContext();
       
        Iterator iterator = context.keySet().iterator();
        while (iterator.hasNext()) {
            String name = (String) iterator.next();
            Object value = context.get(name);
            SOAPElement dataNode
                = contextData.addChildElement(name, Consts.CD_PREFIX);
            maybeAddObjectAsTextNode(respMsg, value, dataNode);
        }
    }

    /**
     * Appends the result data to the given node in a properties request.
     * @param respMsg the message to send
     * @param proc the related process
     * @param propsNode the parent node
     * @throws RemoteException
     *         Error accessing the workflow engine
     * @throws SOAPException
     *         Error creating the message
     * @throws ResultNotAvailableException
     * @throws SAXException
     * @throws TransformerException
     */
    private void appendResultData(SOAPMessage respMsg, Process proc,
            SOAPBodyElement propsNode)
        throws RemoteException, SOAPException, ResultNotAvailableException {
        SOAPElement resultData
            = propsNode.addChildElement("ResultData", Consts.ASAP_PREFIX);
       
        ProcessDefinition procdef = proc.processDefinition();
        ProcessDataInfo resultSignature = procdef.resultSignature();

        resultData.addNamespaceDeclaration
            (Consts.RS_PREFIX, getResourceReference().getNamespace());
       
        ProcessData result;
        result = proc.result();
       
        Iterator iterator = resultSignature.keySet().iterator();
        while (iterator.hasNext()) {
            String name = (String) iterator.next();
            Object value = result.get(name);
            SOAPElement dataNode
                = resultData.addChildElement(name, Consts.RS_PREFIX);
            maybeAddObjectAsTextNode(respMsg, value, dataNode);
        }
    }

    /**
     * Adds the given <code>value</code> as a text node (XSD conform), if it
     * not <code>null</code>.
     * @param message the current message.
     * @param value the object to add.
     * @param parent the parent node.
     * @throws SOAPException
     * @throws TransformerException
     * @throws SAXException
     */
    private void maybeAddObjectAsTextNode
        (SOAPMessage message, Object value, SOAPElement parent)
        throws SOAPException {
        if (value == null) {
            return;
        }
       
        if (value instanceof org.w3c.dom.Element) {
            importW3CNodeAsChild(parent, (org.w3c.dom.Element) value);
        } else if (value instanceof SAXEventBuffer) {
            importSAXAsChild(message, parent, (SAXEventBuffer) value);
        } else {
            String text;
            if (value instanceof Date) {
                text = XMLUtil.toXsdGMTDateTime((Date) value);
            } else {
                text = value.toString();
            }
            parent.addTextNode(text);
        }
    }
   
    /**
     * Sets properties of the process.
     * @param action the action element.
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void setInstanceProperties(SOAPElement action, SOAPMessage reqMsg,
            SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        if (logger.isDebugEnabled()) {
            logger.debug("set instance properties...");
        }
       
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        Process proc = null;
        try {
            proc = (Process) getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        }

        String description = getChildsTextContent(action, "Description");
        if (description != null) {
            proc.setDescription(description);
        }
       
        SOAPElement data = findChildNode(action, "Data");
        if (data != null) {
            ProcessDefinition procdef = proc.processDefinition();
            ProcessData procData;
            try {
                procData = getProcessData(procdef, data);
                proc.setProcessContext(procData);
            } catch (ParseException e) {
                FaultUtils.setFault(respMsg,
                        ASAPException.ASAP_INVALID_INSTANCE_KEY,
                        e.getMessage());
               
                return;
            } catch (InvalidDataException e) {
                FaultUtils.setFault(respMsg,
                        ASAPException.ASAP_INVALID_INSTANCE_KEY,
                        e.getMessage());
               
                return;
            } catch (UpdateNotAllowedException e) {
                FaultUtils.setFault(respMsg,
                        ASAPException.ASAP_INVALID_INSTANCE_KEY,
                        e.getMessage());
               
                return;
            }
        }
       
        SOAPBodyElement propsNode
            = createAsapResponseNode(respMsg, Consts.SET_PROPERTIES_RESPONSE);
   
        try {
            appendInstanceProperties(respMsg, receiverKey, proc, propsNode);
        } catch (ResultNotAvailableException e) {
            FaultUtils.setFault(respMsg, e);
           
            return;
        }
    }

    /**
     * Extracts the process data from the given context data.
     * @param contextData the context data.
     * @return process data to set in the the process.
     * @throws RemoteException
     * @throws SOAPException
     * @throws ParseException
     */
    private ProcessData getProcessData(ProcessDefinition procdef,
            SOAPElement contextData)
        throws RemoteException, SOAPException, ParseException {
        ProcessDataInfo info = procdef.contextSignature();
        ProcessData procData = new DefaultProcessData();
       
        for (Iterator pdi = contextData.getChildElements(); pdi.hasNext();) {
            SOAPElement current = (SOAPElement) pdi.next();
            String pdname = current.getLocalName();
            Object type = info.get(pdname);
            if (type == null) {
                throw new SOAPException("process does not contain a variable "
                                + "with name " + pdname);
            }
            String pdvalue = XMLUtil.getFirstLevelTextContent(current);
           
            Object value;
           
            if (type.equals(String.class)) {
                value = pdvalue;
            } else if (type.equals(Long.class)) {
                value = new Long(pdvalue);
            } else if (type.equals(Double.class)) {
                value = new Double(XMLUtil.parseXsdDouble(pdvalue));
            } else if (type.equals(Boolean.class)) {
                value = new Boolean(XMLUtil.parseXsdBoolean(pdvalue));
            } else if (type.equals(Date.class)) {
                value = XMLUtil.parseXsdDateTime(pdvalue);
            } else if (type.equals(org.w3c.dom.Element.class)) {
                // TODO: Check other values for schema types.
                value = current.getFirstChild();
            } else {
                // cannot resolve the type. leave it as a string and pray.
                value = pdvalue;
            }
           
            procData.put(pdname, value);
        }
       
        return procData;
    }
   
    /**
     * Subscribes to a process instance.
     * @param instanceref the resource reference of the instance
     * @param action the action element.
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void subscribe
        (SOAPElement action, SOAPMessage reqMsg, SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        Process proc = null;
        try {
            proc = (Process) getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY,
                    e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY,
                    e.getMessage());
           
            return;
        }

        String observer = getChildsTextContent(action, "ObserverKey");
        if (observer == null) {
            FaultUtils.setFault(respMsg, ASAPException.ASAP_ELEMENT_MISSING,
                    "Missing observer key!");
            return;
        }

        ProcessDefinition procdef = proc.processDefinition();
        try {
            ObserverRegistry obs = getObserverRegistry();
            obs.subscribe
                (observer, procdef.packageId(),
                 procdef.processId(), proc.key(),
                 getResourceReference().getBaseUrl());
        } catch (SQLException e) {
            FaultUtils.setFault(respMsg, e);
           
            return;
        }
       
        createAsapResponseNode(respMsg, Consts.SUBSCRIBE_RESPONSE);
    }

    /**
     * Unsubscribes from a process instance.
     * @param action the action element.
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void unsubscribe(SOAPElement action, SOAPMessage reqMsg,
            SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        Process proc = null;
        try {
            proc = (Process) getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY,
                    e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY,
                    e.getMessage());
           
            return;
        }

        String observer = getChildsTextContent(action, "ObserverKey");
        if (observer == null) {
            FaultUtils.setFault(respMsg, ASAPException.ASAP_ELEMENT_MISSING,
                    "Missing observer key!");
           
            return;
        }

        ProcessDefinition procdef = proc.processDefinition();
        try {
            ObserverRegistry obs = getObserverRegistry();
            obs.unsubscribe(observer, procdef.packageId(), procdef.processId(),
                    proc.key());
        } catch (SQLException e) {
            FaultUtils.setFault(respMsg, e);
           
            return;
        }
       
        createAsapResponseNode(respMsg, Consts.UNSUBSCRIBE_RESPONSE);
    }

    /**
     * Returns a list of currently active activities.
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void listActivities(SOAPMessage reqMsg, SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        WfProcess proc = null;
        try {
            proc = getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        }
       
        Collection activities;
        try {
            activities = proc.activitiesInState("open.running");
        } catch (InvalidStateException e) {
            FaultUtils.setFault(respMsg, ASAPException.ASAP_OPERATION_FAILED,
                    e.getMessage());
           
            return;
        }

        SOAPBodyElement activitiesNode  = createWfxmlResponseNode(respMsg,
                Consts.LIST_ACTIVITIES_RESPONSE);
        activitiesNode.addNamespaceDeclaration(Consts.ASAP_PREFIX,
                Consts.ASAP_NS);
       
        Iterator iterator = activities.iterator();
        while(iterator.hasNext()) {
            Activity activity = (Activity) iterator.next();
            SOAPElement activityNode
                = activitiesNode.addChildElement("ActivityInfo",
                        Consts.WFXML_PREFIX);
            SOAPElement activityKey
                = activityNode.addChildElement("ActivityKey",
                        Consts.WFXML_PREFIX);
            ResourceReference actResRef = new ResourceReference
                (getResourceReference().getBaseUrl(), activity);
            activityKey.addTextNode (actResRef.getResourceKey());
            SOAPElement name = activityNode.addChildElement("Name",
                    Consts.ASAP_PREFIX);
            String val = activity.name();
            maybeAddTextNode(name, val);
            SOAPElement description = activityNode
                .addChildElement("Description", Consts.ASAP_PREFIX);
            val = activity.description();
            maybeAddTextNode(description, val);
            Collection assignments = activity.assignments();
            Iterator assignmentIterator = assignments.iterator();
            while (assignmentIterator.hasNext()) {
                Assignment assignment = (Assignment) iterator.next();
                WfResource resource = assignment.assignee();
                SOAPElement assignee = activityNode.addChildElement("Assignee",
                        Consts.WFXML_PREFIX);
                val = resource.resourceName();
                if (val == null) {
                    assignee.addTextNode(resource.resourceKey());
                } else {
                    assignee.addTextNode(val);
                }
            }
        }
    }

    /**
     * Changes the state of this process.
     * @param action the element containing the request.
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void changeState(SOAPElement action, SOAPMessage reqMsg,
            SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
       
        WfProcess proc = null;
        try {
            proc = getProcess(receiverKey);
        } catch (InvalidKeyException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        } catch (NoSuchElementException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_INSTANCE_KEY, e.getMessage());
           
            return;
        }
       
       
        String requestedAsapState = getChildsTextContent(action, "State");
        String newAsapState;
        try {
            if (logger.isDebugEnabled()) {
                logger.debug("changing state of " + proc.name() + "/"
                        + proc.key() + " to '" + requestedAsapState + "'...");
            }

            String requestedOmgState
                = StateMapper.asap2omgState(requestedAsapState);

            proc.changeState(requestedOmgState);
           
            String newOmgState = proc.state();
            newAsapState = StateMapper.omg2asapState(newOmgState);
        } catch (InvalidStateException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_CONTEXT_DATA, e.getMessage());
            return;
        } catch (TransitionNotAllowedException e) {
            FaultUtils.setFault(respMsg,
                    ASAPException.ASAP_INVALID_STATE_TRANSITION,
                    e.getMessage());
            return;
        }

        SOAPBodyElement stateNode = createAsapResponseNode(respMsg,
                Consts.CHANGE_STATE_RESPONSE);
        stateNode.addNamespaceDeclaration(Consts.ASAP_PREFIX, Consts.ASAP_NS);
       
        SOAPElement newStateElement
            = stateNode.addChildElement("State", Consts.ASAP_PREFIX);
        newStateElement.addTextNode(newAsapState);
   
        if (logger.isDebugEnabled()) {
            logger.debug("changed state of " + proc.name() + "/" + proc.key()
                    + " from " + requestedAsapState + " to " + newAsapState);
        }
    }

    /**
     * Terminates the process instance.
     *
     * <p>
     * The specification is inconsistent at this point. The WfXML 20
     * specification redirects to the ASAP specification where terminate should
     * be defined. This specification in the ASAP documentation is missing
     * (in the xsd as well). So we leave it unimplemented at this point.
     * If the implementation is more clear, this is the point where the
     * implementation must go.
     * </p>
     *
     * @param reqMsg the request message.
     * @param respMsg the response message
     * @throws SOAPException
     *         error evaluating the request or constructing the response.
     * @throws RemoteException
     *         error accessing the workflow engine.
     */
    private void terminate(SOAPMessage reqMsg, SOAPMessage respMsg)
        throws SOAPException, RemoteException {
        FaultUtils.setFault(respMsg, ASAPException.ASAP_OPERATION_FAILED,
                Consts.TERMINATE_REQUEST
                + " is not implemented due to a missing specification!");
    }

    /**
     * Gets the process manager that that can handle the package id, the
     * process id, and the process key that are encoded in the given uri.
     * @param uri the uri with package id, process id and process key.
     * @return process.
     * @throws RemoteException
     *         error accessing the workflow engine
     * @throws InvalidKeyException
     *         process manager or process does not exist
     */
    private WfProcess getProcess(String uri)
        throws RemoteException, InvalidKeyException {
        String packageId = getResourceReference().getPackageId();
        String processId = getResourceReference().getProcessId();
        String processKey = getResourceReference().getProcessKey();
           
        if (logger.isDebugEnabled()) {
            logger.debug("finding process definition'" + packageId + "/"
                    + processId + "/" + processKey + "'...");
        }
       
        ProcessDirectory pd = getWorkflowService().processDirectory();
       
        WfProcess proc
            = pd.lookupProcess(packageId + "/" + processId, processKey);
       
        return proc;
    }
   
    /**
     * Creates a state changed notification message for the given observer.
     * @param observer the observer
     * @param newState the new state of the process
     * @param oldState  the old state of the process
     * @return notification message.
     * @throws SOAPException
     *         error creating the message.
     */
    public SOAPMessage createStateChangedMessage
        (String observer, String newState, String oldState)
        throws SOAPException {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage message = messageFactory.createMessage();
       
        SOAPEnvelope env = message.getSOAPPart().getEnvelope();
        SOAPHeader header = env.getHeader ();
        SOAPHeaderElement req = header.addHeaderElement
            (env.createName("Request", "as", Consts.ASAP_NS));
        SOAPElement receiverkey
            = req.addChildElement("ReceiverKey", Consts.ASAP_PREFIX);
        receiverkey.addTextNode(observer);
        SOAPElement senderkey = req.addChildElement("SenderKey",
                Consts.ASAP_PREFIX);
        senderkey.addTextNode(getResourceReference().getResourceKey());
        SOAPElement responseRequired = req.addChildElement("ResponseRequired",
                Consts.ASAP_PREFIX);
        responseRequired.addTextNode("No");
       
        SOAPBody body = env.getBody();
        Name respName = env.createName(Consts.STATE_CHANGE_REQUEST,
                Consts.ASAP_PREFIX, Consts.ASAP_NS);

        SOAPBodyElement action = body.addBodyElement(respName);
        SOAPElement newStateNode
            = action.addChildElement("State", Consts.ASAP_PREFIX);
        newStateNode.addTextNode(newState);
        SOAPElement oldStateNode
            = action.addChildElement("PreviousState", Consts.ASAP_PREFIX);
        oldStateNode.addTextNode(oldState);
       
        return message;
    }

    /**
     * Creates a completed notification message for a given observer.
     * @param observer the observer
     * @return notification message.
     * @throws SOAPException
     *         error creating the message.
     * @throws SAXException
     * @throws TransformerException
     * @throws ResultNotAvailableException
     * @throws RemoteException
     */
    public SOAPMessage createCompletedMessage
        (String observer, ProcessData result)
        throws SOAPException, RemoteException {
        MessageFactory messageFactory = MessageFactory.newInstance();
        SOAPMessage message = messageFactory.createMessage();
       
        SOAPEnvelope env = message.getSOAPPart().getEnvelope();
        SOAPHeader header = env.getHeader ();
        SOAPHeaderElement req = header.addHeaderElement
            (env.createName("Request", "as", Consts.ASAP_NS));
        SOAPElement receiverKey
            = req.addChildElement("ReceiverKey", Consts.ASAP_PREFIX);
        receiverKey.addTextNode(observer);
        SOAPElement senderKey = req.addChildElement("SenderKey",
                Consts.ASAP_PREFIX);
        senderKey.addTextNode(getResourceReference().getResourceKey());
        SOAPElement responseRequired = req.addChildElement("ResponseRequired",
                Consts.ASAP_PREFIX);
        responseRequired.addTextNode("No");
       
        SOAPBody body = env.getBody();
        Name respName = env.createName(Consts.COMPLETED_REQUEST,
                Consts.ASAP_PREFIX, Consts.ASAP_NS);

        SOAPBodyElement action = body.addBodyElement(respName);
        appendResultData(message, result, action);
       
        return message;
    }

    /**
     * Appends the result data to the given node in a properties request.
     * @param respMsg the message to send
     * @param result result of the process.
     * @param parent the parent node
     * @throws RemoteException
     *         Error accessing the workflow engine
     * @throws SOAPException
     *         Error creating the message
     * @throws ResultNotAvailableException
     * @throws SAXException
     * @throws TransformerException
     */
    private void appendResultData(SOAPMessage respMsg, ProcessData result,
            SOAPBodyElement parent)
        throws RemoteException, SOAPException  {
        SOAPElement resultData
            = parent.addChildElement("ResultData", Consts.ASAP_PREFIX);
       
        if (result == null) {
            return;
        }
       
        Iterator iterator = result.keySet().iterator();
        while (iterator.hasNext()) {
            String name = (String) iterator.next();
            Object value = result.get(name);
            SOAPElement dataNode
                = resultData.addChildElement(name, Consts.RS_PREFIX);
            maybeAddObjectAsTextNode(respMsg, value, dataNode);
        }
    }
   
}
TOP

Related Classes of de.danet.an.workflow.clients.wfxml.InstanceResponseGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.