0001: /*
0002: * This file is part of the WfMOpen project.
0003: * Copyright (C) 2001-2006 Danet GmbH (www.danet.de), BU BTS.
0004: * All rights reserved.
0005: *
0006: * This program is free software; you can redistribute it and/or modify
0007: * it under the terms of the GNU General Public License as published by
0008: * the Free Software Foundation; either version 2 of the License, or
0009: * (at your option) any later version.
0010: *
0011: * This program is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0014: * GNU General Public License for more details.
0015: *
0016: * You should have received a copy of the GNU General Public License
0017: * along with this program; if not, write to the Free Software
0018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0019: *
0020: * $Id: InstanceResponseGenerator.java,v 1.12 2007/03/29 11:46:54 schnelle Exp $
0021: *
0022: * $Log: InstanceResponseGenerator.java,v $
0023: * Revision 1.12 2007/03/29 11:46:54 schnelle
0024: * Reactivated ASAPException to propagate ASAP error messages in cases of an invalid key, a missing resource or an invalid factory.
0025: *
0026: * Revision 1.11 2007/03/27 21:59:42 mlipp
0027: * Fixed lots of checkstyle warnings.
0028: *
0029: * Revision 1.10 2007/03/01 12:32:57 schnelle
0030: * Enhanced Instance.SetProperties to process ContextData.
0031: *
0032: * Revision 1.9 2007/02/16 21:00:21 mlipp
0033: * Fixed some null pointer problems.
0034: *
0035: * Revision 1.8 2007/02/01 13:44:43 schnelle
0036: * Using namespace for factory schemas that do not contain '&'.
0037: *
0038: * Revision 1.7 2007/02/01 12:38:36 schnelle
0039: * Use of encoded key for properties.
0040: *
0041: * Revision 1.6 2007/01/31 22:55:36 mlipp
0042: * Some more refactoring and fixes of problems introduced by refactoring.
0043: *
0044: * Revision 1.5 2007/01/31 14:53:06 schnelle
0045: * Small corrections wvaluating the resource reference.
0046: *
0047: * Revision 1.4 2007/01/31 12:24:06 drmlipp
0048: * Design revisited.
0049: *
0050: * Revision 1.3 2007/01/30 15:07:37 schnelle
0051: * Filtering of all instances for the current factory in a list instances request.
0052: *
0053: * Revision 1.2 2007/01/30 11:56:14 drmlipp
0054: * Merged Wf-XML branch.
0055: *
0056: * Revision 1.1.2.23 2007/01/29 15:04:19 schnelle
0057: * Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
0058: *
0059: * Revision 1.1.2.22 2007/01/29 13:40:30 schnelle
0060: * Storing of the sender base in the servlet context.
0061: *
0062: * Revision 1.1.2.21 2007/01/29 13:10:27 drmlipp
0063: * Using utilities method for timestamp convertion.
0064: *
0065: * Revision 1.1.2.20 2007/01/24 11:46:35 schnelle
0066: * Moved wsdl files and xsd files intot resources subdirectory.
0067: *
0068: * Revision 1.1.2.19 2007/01/24 10:56:50 schnelle
0069: * Prepared return of a result for aobservers.
0070: *
0071: * Revision 1.1.2.18 2007/01/24 09:28:53 schnelle
0072: * Returning result data for the instance.
0073: *
0074: * Revision 1.1.2.17 2007/01/19 15:01:59 schnelle
0075: * Returning context data at instance getproperties.
0076: *
0077: * Revision 1.1.2.16 2007/01/19 12:34:56 schnelle
0078: * Moved generation and decoding of the URI that is used as the receiver key to new class URIDecoder.
0079: *
0080: * Revision 1.1.2.15 2007/01/16 11:05:42 schnelle
0081: * Refactoring: Moved subscription handling methods to own class.
0082: *
0083: * Revision 1.1.2.14 2007/01/11 11:37:10 schnelle
0084: * Added subscription if an oberver key is given in the creation of a process.
0085: *
0086: * Revision 1.1.2.13 2007/01/10 14:48:18 schnelle
0087: * Checked if a subscription already exists, before storing the observer in the database.
0088: *
0089: * Revision 1.1.2.12 2007/01/10 14:16:52 schnelle
0090: * Implemented unsubscribe.
0091: *
0092: * Revision 1.1.2.11 2007/01/10 13:41:27 schnelle
0093: * Implemented subscribe.
0094: *
0095: * Revision 1.1.2.10 2007/01/10 09:03:43 schnelle
0096: * Implemented set properties methods.
0097: *
0098: * Revision 1.1.2.9 2006/12/20 13:32:24 schnelle
0099: * Basic implementato of GetProperties for Instance and Activity.
0100: *
0101: * Revision 1.1.2.8 2006/12/18 14:41:02 schnelle
0102: * Preparatation for individual schema definition for each getproperties request.
0103: *
0104: * Revision 1.1.2.7 2006/12/14 08:50:21 schnelle
0105: * Implemented CompleteActivity.
0106: *
0107: * Revision 1.1.2.6 2006/12/13 11:23:48 schnelle
0108: * Implemented instance ListActivities.
0109: *
0110: * Revision 1.1.2.5 2006/12/12 13:24:38 schnelle
0111: * Introduction of ASAPException to provide a detailed mesage.
0112: *
0113: * Revision 1.1.2.4 2006/12/12 11:41:21 schnelle
0114: * Implemented state mapping of omg states to asap states.
0115: *
0116: * Revision 1.1.2.3 2006/12/12 09:34:35 schnelle
0117: * Implemented ChangeState for Instance.
0118: *
0119: * Revision 1.1.2.2 2006/12/11 11:05:34 schnelle
0120: * Added template methods for all requests.
0121: *
0122: * Revision 1.1.2.1 2006/11/28 12:20:10 schnelle
0123: * Creation of a separate class to handle the issues for a specific resource.
0124: *
0125: * Revision 1.1.2.2 2006/11/27 15:41:55 schnelle
0126: * Introducing some constants for request and response identification.
0127: *
0128: * Revision 1.1.2.1 2006/11/24 12:19:13 schnelle
0129: * Separtion of response generation into ResponseGenerator class.
0130: *
0131: */
0132: package de.danet.an.workflow.clients.wfxml;
0133:
0134: import java.rmi.RemoteException;
0135: import java.sql.SQLException;
0136: import java.text.ParseException;
0137: import java.util.Collection;
0138: import java.util.Date;
0139: import java.util.Iterator;
0140: import java.util.NoSuchElementException;
0141:
0142: import javax.xml.soap.MessageFactory;
0143: import javax.xml.soap.Name;
0144: import javax.xml.soap.SOAPBody;
0145: import javax.xml.soap.SOAPBodyElement;
0146: import javax.xml.soap.SOAPElement;
0147: import javax.xml.soap.SOAPEnvelope;
0148: import javax.xml.soap.SOAPException;
0149: import javax.xml.soap.SOAPHeader;
0150: import javax.xml.soap.SOAPHeaderElement;
0151: import javax.xml.soap.SOAPMessage;
0152: import javax.xml.transform.TransformerException;
0153:
0154: import org.xml.sax.SAXException;
0155:
0156: import de.danet.an.util.XMLUtil;
0157: import de.danet.an.workflow.api.Activity;
0158: import de.danet.an.workflow.api.DefaultProcessData;
0159: import de.danet.an.workflow.api.InvalidKeyException;
0160: import de.danet.an.workflow.api.Process;
0161: import de.danet.an.workflow.api.ProcessDefinition;
0162: import de.danet.an.workflow.api.ProcessDirectory;
0163: import de.danet.an.workflow.api.SAXEventBuffer;
0164: import de.danet.an.workflow.api.WorkflowService;
0165: import de.danet.an.workflow.assignment.Assignment;
0166: import de.danet.an.workflow.clients.wfxml.ObserverRegistry.ObserverInfo;
0167: import de.danet.an.workflow.omgcore.HistoryNotAvailableException;
0168: import de.danet.an.workflow.omgcore.InvalidDataException;
0169: import de.danet.an.workflow.omgcore.InvalidStateException;
0170: import de.danet.an.workflow.omgcore.ProcessData;
0171: import de.danet.an.workflow.omgcore.ProcessDataInfo;
0172: import de.danet.an.workflow.omgcore.ResultNotAvailableException;
0173: import de.danet.an.workflow.omgcore.TransitionNotAllowedException;
0174: import de.danet.an.workflow.omgcore.UpdateNotAllowedException;
0175: import de.danet.an.workflow.omgcore.WfAuditEvent;
0176: import de.danet.an.workflow.omgcore.WfCreateProcessAuditEvent;
0177: import de.danet.an.workflow.omgcore.WfProcess;
0178: import de.danet.an.workflow.omgcore.WfResource;
0179: import de.danet.an.workflow.omgcore.WfStateAuditEvent;
0180:
0181: /**
0182: * This class provides the methods of an {@link AbstractResponseGenerator}
0183: * that are relevant for the instance.
0184: *
0185: * <p>
0186: * The <em>Instance</em> resource is the actual "performance of work"; it is the
0187: * process instance. It embodies the context information that distinguishes one
0188: * process instance from another. Some people call this a "case". A process
0189: * instance resource can be used only once: it is created, then it can be
0190: * started, it can be paused, resumed, terminated. If things go normally, it
0191: * will eventually complete.
0192: * </p>
0193: *
0194: * @author Dirk Schnelle
0195: *
0196: */
0197: class InstanceResponseGenerator extends AbstractResponseGenerator {
0198: /** Logger instance. */
0199: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0200: .getLog(InstanceResponseGenerator.class);
0201:
0202: /**
0203: * Constructs a new object.
0204: *
0205: * @param observerRegistry the observer registry.
0206: * @param wfs Reference to the workflow engine.
0207: * @param resRef the resource reference.
0208: */
0209: public InstanceResponseGenerator(ObserverRegistry observerRegistry,
0210: WorkflowService wfs, ResourceReference resRef) {
0211: super (observerRegistry, wfs, resRef);
0212: }
0213:
0214: /* (non-Javadoc)
0215: * @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator
0216: */
0217: public void evaluate(SOAPMessage reqMsg, SOAPMessage respMsg)
0218: throws SOAPException, RemoteException {
0219: SOAPBodyElement actionElement = getActionElement(reqMsg);
0220:
0221: String actName = actionElement.getElementName().getLocalName();
0222:
0223: if (actName.equals(Consts.GET_PROPERTIES_REQUEST)) {
0224: getInstanceProperties(reqMsg, respMsg);
0225: } else if (actName.equals(Consts.SET_PROPERTIES_REQUEST)) {
0226: setInstanceProperties(actionElement, reqMsg, respMsg);
0227: } else if (actName.equals(Consts.SUBSCRIBE_REQUEST)) {
0228: subscribe(actionElement, reqMsg, respMsg);
0229: } else if (actName.equals(Consts.UNSUBSCRIBE_REQUEST)) {
0230: unsubscribe(actionElement, reqMsg, respMsg);
0231: } else if (actName.equals(Consts.LIST_ACTIVITIES_REQUEST)) {
0232: listActivities(reqMsg, respMsg);
0233: } else if (actName.equals(Consts.CHANGE_STATE_REQUEST)) {
0234: changeState(actionElement, reqMsg, respMsg);
0235: } else if (actName.equals(Consts.TERMINATE_REQUEST)) {
0236: terminate(reqMsg, respMsg);
0237: } else {
0238: if (logger.isDebugEnabled()) {
0239: logger.debug("unknown action '" + actName + "'");
0240: }
0241:
0242: FaultUtils.setFault(respMsg,
0243: ASAPException.ASAP_INVALID_OPERATION_SPECIFICATION,
0244: getResourceName() + ": Unknown action \"" + actName
0245: + "\".");
0246: }
0247: }
0248:
0249: /* (non-Javadoc)
0250: * @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator#getSender()
0251: */
0252: protected String getResourceName() {
0253: return RESOURCE_INSTANCE;
0254: }
0255:
0256: /**
0257: * Creates a response that contains properties of the process instance.
0258: *
0259: * @param reqMsg the request message.
0260: * @param respMsg the response message
0261: * @throws SOAPException
0262: * error evaluating the request or constructing the response.
0263: * @throws RemoteException
0264: * error accessing the workflow engine.
0265: */
0266: private void getInstanceProperties(SOAPMessage reqMsg,
0267: SOAPMessage respMsg) throws SOAPException, RemoteException {
0268: if (logger.isDebugEnabled()) {
0269: logger.debug("get instance properties...");
0270: }
0271:
0272: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0273:
0274: Process proc = null;
0275: try {
0276: proc = (Process) getProcess(receiverKey);
0277: } catch (InvalidKeyException e) {
0278: FaultUtils.setFault(respMsg,
0279: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0280: .getMessage());
0281:
0282: return;
0283: } catch (NoSuchElementException e) {
0284: FaultUtils.setFault(respMsg,
0285: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0286: .getMessage());
0287:
0288: return;
0289: }
0290:
0291: SOAPBodyElement propsNode = createAsapResponseNode(respMsg,
0292: Consts.GET_PROPERTIES_RESPONSE);
0293:
0294: try {
0295: appendInstanceProperties(respMsg, receiverKey, proc,
0296: propsNode);
0297: } catch (ResultNotAvailableException e) {
0298: FaultUtils.setFault(respMsg, e);
0299:
0300: return;
0301: }
0302: }
0303:
0304: /**
0305: * Reads the properties from the process instance and appends them to the
0306: * parent.
0307: * @param respMsg the response message.
0308: * @param receiverKey this activity instance
0309: * @param parent the parent node of the response message.
0310: * @param proc the process
0311: * @throws SOAPException
0312: * error appending to the parent node
0313: * @throws RemoteException
0314: * error accessing the process
0315: * @throws SAXException
0316: * @throws TransformerException
0317: * @throws ResultNotAvailableException
0318: */
0319: private void appendInstanceProperties(SOAPMessage respMsg,
0320: String receiverKey, Process proc, SOAPBodyElement propsNode)
0321: throws SOAPException, RemoteException,
0322: ResultNotAvailableException {
0323: SOAPElement keyNode = propsNode.addChildElement("Key",
0324: Consts.ASAP_PREFIX);
0325: keyNode.addTextNode(getResourceReference().getResourceKey());
0326: SOAPElement state = propsNode.addChildElement("State",
0327: Consts.ASAP_PREFIX);
0328: String procState = proc.state();
0329: state.addTextNode(StateMapper.omg2asapState(procState));
0330: SOAPElement nameNode = propsNode.addChildElement("Name",
0331: Consts.ASAP_PREFIX);
0332: nameNode.addTextNode(proc.name());
0333: SOAPElement subjectNode = propsNode.addChildElement("Subject",
0334: Consts.ASAP_PREFIX);
0335: if (proc.description() != null) {
0336: subjectNode.addTextNode(proc.description());
0337: }
0338: SOAPElement descNode = propsNode.addChildElement("Description",
0339: Consts.ASAP_PREFIX);
0340: if (proc.description() != null) {
0341: descNode.addTextNode(proc.description());
0342: }
0343: SOAPElement factoryKey = propsNode.addChildElement(
0344: "FactoryKey", Consts.ASAP_PREFIX);
0345: ResourceReference procDefRes = new ResourceReference(
0346: getResourceReference().getBaseUrl(), proc
0347: .processDefinition());
0348: factoryKey.addTextNode(procDefRes.getResourceKey());
0349:
0350: appendObservers(respMsg, proc, propsNode);
0351: appendHistory(respMsg, proc, propsNode);
0352: appendContextData(respMsg, proc, propsNode);
0353: appendResultData(respMsg, proc, propsNode);
0354: }
0355:
0356: /**
0357: * Appends the list of observers to the given node in a properties request.
0358: * @param respMsg the message to send
0359: * @param proc the related process
0360: * @param propsNode the parent node
0361: * @throws RemoteException
0362: * Error accessing the workflow engine
0363: * @throws SOAPException
0364: * Error creating the message
0365: */
0366: private void appendObservers(SOAPMessage respMsg, Process proc,
0367: SOAPBodyElement propsNode) throws RemoteException,
0368: SOAPException {
0369: Collection observers;
0370: try {
0371: ObserverRegistry obs = getObserverRegistry();
0372: ProcessDefinition procdef = proc.processDefinition();
0373: observers = obs.getObservers(procdef.packageId(), procdef
0374: .processId(), proc.key());
0375: } catch (SQLException e) {
0376: FaultUtils.setFault(respMsg, e);
0377: return;
0378: }
0379:
0380: if (observers.size() > 0) {
0381: SOAPElement observersNode = propsNode.addChildElement(
0382: "Observers", Consts.ASAP_PREFIX);
0383: Iterator iterator = observers.iterator();
0384: while (iterator.hasNext()) {
0385: ObserverInfo observerInfo = (ObserverInfo) iterator
0386: .next();
0387: SOAPElement observerKey = observersNode
0388: .addChildElement("ObserverKey",
0389: Consts.ASAP_PREFIX);
0390: observerKey.addTextNode(observerInfo.getObserverKey());
0391:
0392: }
0393: }
0394: }
0395:
0396: /**
0397: * Appends the history of the process to the given node in a properties
0398: * request.
0399: * @param respMsg the message to send
0400: * @param proc the related process
0401: * @param propsNode the parent node
0402: * @throws RemoteException
0403: * Error accessing the workflow engine
0404: * @throws SOAPException
0405: * Error creating the message
0406: */
0407: private void appendHistory(SOAPMessage respMsg, Process proc,
0408: SOAPBodyElement propsNode) throws RemoteException,
0409: SOAPException {
0410: SOAPElement historyNode = propsNode.addChildElement("History",
0411: Consts.ASAP_PREFIX);
0412:
0413: Collection history;
0414: try {
0415: history = proc.history();
0416: } catch (HistoryNotAvailableException e) {
0417: // well, if there is nothing to append...
0418: return;
0419: }
0420: Iterator iterator = history.iterator();
0421: while (iterator.hasNext()) {
0422: WfAuditEvent event = (WfAuditEvent) iterator.next();
0423:
0424: String asapEventType = getAsapEventType(event);
0425: if (asapEventType == null) {
0426: continue;
0427: }
0428:
0429: SOAPElement eventNode = historyNode.addChildElement(
0430: "Event", Consts.ASAP_PREFIX);
0431: Date date = event.timeStamp();
0432: SOAPElement timeNode = eventNode.addChildElement("Time",
0433: Consts.ASAP_PREFIX);
0434: timeNode.addTextNode(XMLUtil.toXsdGMTDateTime(date));
0435: SOAPElement type = eventNode.addChildElement("EventType",
0436: Consts.ASAP_PREFIX);
0437: type.addTextNode(asapEventType);
0438: SOAPElement sourceKey = eventNode.addChildElement(
0439: "SourceKey", Consts.ASAP_PREFIX);
0440: sourceKey.addTextNode("");
0441: SOAPElement details = eventNode.addChildElement("Details",
0442: Consts.ASAP_PREFIX);
0443: details.addTextNode("");
0444: String newState;
0445: String oldState;
0446:
0447: if (event instanceof WfCreateProcessAuditEvent) {
0448: // The old state is not defined, and we are not allowed to
0449: // add an empty string.
0450: oldState = "open.notrunning";
0451: newState = "open.notrunning";
0452: } else {
0453: WfStateAuditEvent stateEvent = (WfStateAuditEvent) event;
0454: oldState = StateMapper.omg2asapState(stateEvent
0455: .oldState());
0456: newState = StateMapper.omg2asapState(stateEvent
0457: .newState());
0458: }
0459: SOAPElement oldStateNode = eventNode.addChildElement(
0460: "OldState", Consts.ASAP_PREFIX);
0461: oldStateNode.addTextNode(oldState);
0462: SOAPElement newStateNode = eventNode.addChildElement(
0463: "NewState", Consts.ASAP_PREFIX);
0464: newStateNode.addTextNode(newState);
0465:
0466: }
0467: }
0468:
0469: /**
0470: * Gets the ASAP description for the {@link WfAuditEvent}.
0471: * @param event the event
0472: * @return ASAP representation of the event or <code>null</null>
0473: * if no corresponding ASAP event exists..
0474: */
0475: private String getAsapEventType(WfAuditEvent event) {
0476: String type = event.eventType();
0477: if (type.equals(WfAuditEvent.PROCESS_CREATED)) {
0478: return "InstanceCreated";
0479: }
0480:
0481: if (type.equals(WfAuditEvent.PROCESS_STATE_CHANGED)) {
0482: return "StateChanged";
0483: }
0484: return null;
0485: }
0486:
0487: /**
0488: * Appends the context data to the given node in a properties request.
0489: * @param respMsg the message to send
0490: * @param proc the related process
0491: * @param propsNode the parent node
0492: * @throws RemoteException
0493: * Error accessing the workflow engine
0494: * @throws SOAPException
0495: * Error creating the message
0496: * @throws SAXException
0497: * @throws TransformerException
0498: */
0499: private void appendContextData(SOAPMessage respMsg, Process proc,
0500: SOAPBodyElement propsNode) throws RemoteException,
0501: SOAPException {
0502: SOAPElement contextData = propsNode.addChildElement(
0503: "ContextData", Consts.ASAP_PREFIX);
0504:
0505: contextData.addNamespaceDeclaration(Consts.CD_PREFIX,
0506: getResourceReference().getNamespace());
0507:
0508: ProcessData context = proc.processContext();
0509:
0510: Iterator iterator = context.keySet().iterator();
0511: while (iterator.hasNext()) {
0512: String name = (String) iterator.next();
0513: Object value = context.get(name);
0514: SOAPElement dataNode = contextData.addChildElement(name,
0515: Consts.CD_PREFIX);
0516: maybeAddObjectAsTextNode(respMsg, value, dataNode);
0517: }
0518: }
0519:
0520: /**
0521: * Appends the result data to the given node in a properties request.
0522: * @param respMsg the message to send
0523: * @param proc the related process
0524: * @param propsNode the parent node
0525: * @throws RemoteException
0526: * Error accessing the workflow engine
0527: * @throws SOAPException
0528: * Error creating the message
0529: * @throws ResultNotAvailableException
0530: * @throws SAXException
0531: * @throws TransformerException
0532: */
0533: private void appendResultData(SOAPMessage respMsg, Process proc,
0534: SOAPBodyElement propsNode) throws RemoteException,
0535: SOAPException, ResultNotAvailableException {
0536: SOAPElement resultData = propsNode.addChildElement(
0537: "ResultData", Consts.ASAP_PREFIX);
0538:
0539: ProcessDefinition procdef = proc.processDefinition();
0540: ProcessDataInfo resultSignature = procdef.resultSignature();
0541:
0542: resultData.addNamespaceDeclaration(Consts.RS_PREFIX,
0543: getResourceReference().getNamespace());
0544:
0545: ProcessData result;
0546: result = proc.result();
0547:
0548: Iterator iterator = resultSignature.keySet().iterator();
0549: while (iterator.hasNext()) {
0550: String name = (String) iterator.next();
0551: Object value = result.get(name);
0552: SOAPElement dataNode = resultData.addChildElement(name,
0553: Consts.RS_PREFIX);
0554: maybeAddObjectAsTextNode(respMsg, value, dataNode);
0555: }
0556: }
0557:
0558: /**
0559: * Adds the given <code>value</code> as a text node (XSD conform), if it
0560: * not <code>null</code>.
0561: * @param message the current message.
0562: * @param value the object to add.
0563: * @param parent the parent node.
0564: * @throws SOAPException
0565: * @throws TransformerException
0566: * @throws SAXException
0567: */
0568: private void maybeAddObjectAsTextNode(SOAPMessage message,
0569: Object value, SOAPElement parent) throws SOAPException {
0570: if (value == null) {
0571: return;
0572: }
0573:
0574: if (value instanceof org.w3c.dom.Element) {
0575: importW3CNodeAsChild(parent, (org.w3c.dom.Element) value);
0576: } else if (value instanceof SAXEventBuffer) {
0577: importSAXAsChild(message, parent, (SAXEventBuffer) value);
0578: } else {
0579: String text;
0580: if (value instanceof Date) {
0581: text = XMLUtil.toXsdGMTDateTime((Date) value);
0582: } else {
0583: text = value.toString();
0584: }
0585: parent.addTextNode(text);
0586: }
0587: }
0588:
0589: /**
0590: * Sets properties of the process.
0591: * @param action the action element.
0592: * @param reqMsg the request message.
0593: * @param respMsg the response message
0594: * @throws SOAPException
0595: * error evaluating the request or constructing the response.
0596: * @throws RemoteException
0597: * error accessing the workflow engine.
0598: */
0599: private void setInstanceProperties(SOAPElement action,
0600: SOAPMessage reqMsg, SOAPMessage respMsg)
0601: throws SOAPException, RemoteException {
0602: if (logger.isDebugEnabled()) {
0603: logger.debug("set instance properties...");
0604: }
0605:
0606: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0607:
0608: Process proc = null;
0609: try {
0610: proc = (Process) getProcess(receiverKey);
0611: } catch (InvalidKeyException e) {
0612: FaultUtils.setFault(respMsg,
0613: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0614: .getMessage());
0615:
0616: return;
0617: } catch (NoSuchElementException e) {
0618: FaultUtils.setFault(respMsg,
0619: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0620: .getMessage());
0621:
0622: return;
0623: }
0624:
0625: String description = getChildsTextContent(action, "Description");
0626: if (description != null) {
0627: proc.setDescription(description);
0628: }
0629:
0630: SOAPElement data = findChildNode(action, "Data");
0631: if (data != null) {
0632: ProcessDefinition procdef = proc.processDefinition();
0633: ProcessData procData;
0634: try {
0635: procData = getProcessData(procdef, data);
0636: proc.setProcessContext(procData);
0637: } catch (ParseException e) {
0638: FaultUtils.setFault(respMsg,
0639: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0640: .getMessage());
0641:
0642: return;
0643: } catch (InvalidDataException e) {
0644: FaultUtils.setFault(respMsg,
0645: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0646: .getMessage());
0647:
0648: return;
0649: } catch (UpdateNotAllowedException e) {
0650: FaultUtils.setFault(respMsg,
0651: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0652: .getMessage());
0653:
0654: return;
0655: }
0656: }
0657:
0658: SOAPBodyElement propsNode = createAsapResponseNode(respMsg,
0659: Consts.SET_PROPERTIES_RESPONSE);
0660:
0661: try {
0662: appendInstanceProperties(respMsg, receiverKey, proc,
0663: propsNode);
0664: } catch (ResultNotAvailableException e) {
0665: FaultUtils.setFault(respMsg, e);
0666:
0667: return;
0668: }
0669: }
0670:
0671: /**
0672: * Extracts the process data from the given context data.
0673: * @param contextData the context data.
0674: * @return process data to set in the the process.
0675: * @throws RemoteException
0676: * @throws SOAPException
0677: * @throws ParseException
0678: */
0679: private ProcessData getProcessData(ProcessDefinition procdef,
0680: SOAPElement contextData) throws RemoteException,
0681: SOAPException, ParseException {
0682: ProcessDataInfo info = procdef.contextSignature();
0683: ProcessData procData = new DefaultProcessData();
0684:
0685: for (Iterator pdi = contextData.getChildElements(); pdi
0686: .hasNext();) {
0687: SOAPElement current = (SOAPElement) pdi.next();
0688: String pdname = current.getLocalName();
0689: Object type = info.get(pdname);
0690: if (type == null) {
0691: throw new SOAPException(
0692: "process does not contain a variable "
0693: + "with name " + pdname);
0694: }
0695: String pdvalue = XMLUtil.getFirstLevelTextContent(current);
0696:
0697: Object value;
0698:
0699: if (type.equals(String.class)) {
0700: value = pdvalue;
0701: } else if (type.equals(Long.class)) {
0702: value = new Long(pdvalue);
0703: } else if (type.equals(Double.class)) {
0704: value = new Double(XMLUtil.parseXsdDouble(pdvalue));
0705: } else if (type.equals(Boolean.class)) {
0706: value = new Boolean(XMLUtil.parseXsdBoolean(pdvalue));
0707: } else if (type.equals(Date.class)) {
0708: value = XMLUtil.parseXsdDateTime(pdvalue);
0709: } else if (type.equals(org.w3c.dom.Element.class)) {
0710: // TODO: Check other values for schema types.
0711: value = current.getFirstChild();
0712: } else {
0713: // cannot resolve the type. leave it as a string and pray.
0714: value = pdvalue;
0715: }
0716:
0717: procData.put(pdname, value);
0718: }
0719:
0720: return procData;
0721: }
0722:
0723: /**
0724: * Subscribes to a process instance.
0725: * @param instanceref the resource reference of the instance
0726: * @param action the action element.
0727: * @param reqMsg the request message.
0728: * @param respMsg the response message
0729: * @throws SOAPException
0730: * error evaluating the request or constructing the response.
0731: * @throws RemoteException
0732: * error accessing the workflow engine.
0733: */
0734: private void subscribe(SOAPElement action, SOAPMessage reqMsg,
0735: SOAPMessage respMsg) throws SOAPException, RemoteException {
0736: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0737:
0738: Process proc = null;
0739: try {
0740: proc = (Process) getProcess(receiverKey);
0741: } catch (InvalidKeyException e) {
0742: FaultUtils.setFault(respMsg,
0743: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0744: .getMessage());
0745:
0746: return;
0747: } catch (NoSuchElementException e) {
0748: FaultUtils.setFault(respMsg,
0749: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0750: .getMessage());
0751:
0752: return;
0753: }
0754:
0755: String observer = getChildsTextContent(action, "ObserverKey");
0756: if (observer == null) {
0757: FaultUtils.setFault(respMsg,
0758: ASAPException.ASAP_ELEMENT_MISSING,
0759: "Missing observer key!");
0760: return;
0761: }
0762:
0763: ProcessDefinition procdef = proc.processDefinition();
0764: try {
0765: ObserverRegistry obs = getObserverRegistry();
0766: obs.subscribe(observer, procdef.packageId(), procdef
0767: .processId(), proc.key(), getResourceReference()
0768: .getBaseUrl());
0769: } catch (SQLException e) {
0770: FaultUtils.setFault(respMsg, e);
0771:
0772: return;
0773: }
0774:
0775: createAsapResponseNode(respMsg, Consts.SUBSCRIBE_RESPONSE);
0776: }
0777:
0778: /**
0779: * Unsubscribes from a process instance.
0780: * @param action the action element.
0781: * @param reqMsg the request message.
0782: * @param respMsg the response message
0783: * @throws SOAPException
0784: * error evaluating the request or constructing the response.
0785: * @throws RemoteException
0786: * error accessing the workflow engine.
0787: */
0788: private void unsubscribe(SOAPElement action, SOAPMessage reqMsg,
0789: SOAPMessage respMsg) throws SOAPException, RemoteException {
0790: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0791:
0792: Process proc = null;
0793: try {
0794: proc = (Process) getProcess(receiverKey);
0795: } catch (InvalidKeyException e) {
0796: FaultUtils.setFault(respMsg,
0797: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0798: .getMessage());
0799:
0800: return;
0801: } catch (NoSuchElementException e) {
0802: FaultUtils.setFault(respMsg,
0803: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0804: .getMessage());
0805:
0806: return;
0807: }
0808:
0809: String observer = getChildsTextContent(action, "ObserverKey");
0810: if (observer == null) {
0811: FaultUtils.setFault(respMsg,
0812: ASAPException.ASAP_ELEMENT_MISSING,
0813: "Missing observer key!");
0814:
0815: return;
0816: }
0817:
0818: ProcessDefinition procdef = proc.processDefinition();
0819: try {
0820: ObserverRegistry obs = getObserverRegistry();
0821: obs.unsubscribe(observer, procdef.packageId(), procdef
0822: .processId(), proc.key());
0823: } catch (SQLException e) {
0824: FaultUtils.setFault(respMsg, e);
0825:
0826: return;
0827: }
0828:
0829: createAsapResponseNode(respMsg, Consts.UNSUBSCRIBE_RESPONSE);
0830: }
0831:
0832: /**
0833: * Returns a list of currently active activities.
0834: * @param reqMsg the request message.
0835: * @param respMsg the response message
0836: * @throws SOAPException
0837: * error evaluating the request or constructing the response.
0838: * @throws RemoteException
0839: * error accessing the workflow engine.
0840: */
0841: private void listActivities(SOAPMessage reqMsg, SOAPMessage respMsg)
0842: throws SOAPException, RemoteException {
0843: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0844:
0845: WfProcess proc = null;
0846: try {
0847: proc = getProcess(receiverKey);
0848: } catch (InvalidKeyException e) {
0849: FaultUtils.setFault(respMsg,
0850: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0851: .getMessage());
0852:
0853: return;
0854: } catch (NoSuchElementException e) {
0855: FaultUtils.setFault(respMsg,
0856: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0857: .getMessage());
0858:
0859: return;
0860: }
0861:
0862: Collection activities;
0863: try {
0864: activities = proc.activitiesInState("open.running");
0865: } catch (InvalidStateException e) {
0866: FaultUtils
0867: .setFault(respMsg,
0868: ASAPException.ASAP_OPERATION_FAILED, e
0869: .getMessage());
0870:
0871: return;
0872: }
0873:
0874: SOAPBodyElement activitiesNode = createWfxmlResponseNode(
0875: respMsg, Consts.LIST_ACTIVITIES_RESPONSE);
0876: activitiesNode.addNamespaceDeclaration(Consts.ASAP_PREFIX,
0877: Consts.ASAP_NS);
0878:
0879: Iterator iterator = activities.iterator();
0880: while (iterator.hasNext()) {
0881: Activity activity = (Activity) iterator.next();
0882: SOAPElement activityNode = activitiesNode.addChildElement(
0883: "ActivityInfo", Consts.WFXML_PREFIX);
0884: SOAPElement activityKey = activityNode.addChildElement(
0885: "ActivityKey", Consts.WFXML_PREFIX);
0886: ResourceReference actResRef = new ResourceReference(
0887: getResourceReference().getBaseUrl(), activity);
0888: activityKey.addTextNode(actResRef.getResourceKey());
0889: SOAPElement name = activityNode.addChildElement("Name",
0890: Consts.ASAP_PREFIX);
0891: String val = activity.name();
0892: maybeAddTextNode(name, val);
0893: SOAPElement description = activityNode.addChildElement(
0894: "Description", Consts.ASAP_PREFIX);
0895: val = activity.description();
0896: maybeAddTextNode(description, val);
0897: Collection assignments = activity.assignments();
0898: Iterator assignmentIterator = assignments.iterator();
0899: while (assignmentIterator.hasNext()) {
0900: Assignment assignment = (Assignment) iterator.next();
0901: WfResource resource = assignment.assignee();
0902: SOAPElement assignee = activityNode.addChildElement(
0903: "Assignee", Consts.WFXML_PREFIX);
0904: val = resource.resourceName();
0905: if (val == null) {
0906: assignee.addTextNode(resource.resourceKey());
0907: } else {
0908: assignee.addTextNode(val);
0909: }
0910: }
0911: }
0912: }
0913:
0914: /**
0915: * Changes the state of this process.
0916: * @param action the element containing the request.
0917: * @param reqMsg the request message.
0918: * @param respMsg the response message
0919: * @throws SOAPException
0920: * error evaluating the request or constructing the response.
0921: * @throws RemoteException
0922: * error accessing the workflow engine.
0923: */
0924: private void changeState(SOAPElement action, SOAPMessage reqMsg,
0925: SOAPMessage respMsg) throws SOAPException, RemoteException {
0926: String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
0927:
0928: WfProcess proc = null;
0929: try {
0930: proc = getProcess(receiverKey);
0931: } catch (InvalidKeyException e) {
0932: FaultUtils.setFault(respMsg,
0933: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0934: .getMessage());
0935:
0936: return;
0937: } catch (NoSuchElementException e) {
0938: FaultUtils.setFault(respMsg,
0939: ASAPException.ASAP_INVALID_INSTANCE_KEY, e
0940: .getMessage());
0941:
0942: return;
0943: }
0944:
0945: String requestedAsapState = getChildsTextContent(action,
0946: "State");
0947: String newAsapState;
0948: try {
0949: if (logger.isDebugEnabled()) {
0950: logger.debug("changing state of " + proc.name() + "/"
0951: + proc.key() + " to '" + requestedAsapState
0952: + "'...");
0953: }
0954:
0955: String requestedOmgState = StateMapper
0956: .asap2omgState(requestedAsapState);
0957:
0958: proc.changeState(requestedOmgState);
0959:
0960: String newOmgState = proc.state();
0961: newAsapState = StateMapper.omg2asapState(newOmgState);
0962: } catch (InvalidStateException e) {
0963: FaultUtils.setFault(respMsg,
0964: ASAPException.ASAP_INVALID_CONTEXT_DATA, e
0965: .getMessage());
0966: return;
0967: } catch (TransitionNotAllowedException e) {
0968: FaultUtils.setFault(respMsg,
0969: ASAPException.ASAP_INVALID_STATE_TRANSITION, e
0970: .getMessage());
0971: return;
0972: }
0973:
0974: SOAPBodyElement stateNode = createAsapResponseNode(respMsg,
0975: Consts.CHANGE_STATE_RESPONSE);
0976: stateNode.addNamespaceDeclaration(Consts.ASAP_PREFIX,
0977: Consts.ASAP_NS);
0978:
0979: SOAPElement newStateElement = stateNode.addChildElement(
0980: "State", Consts.ASAP_PREFIX);
0981: newStateElement.addTextNode(newAsapState);
0982:
0983: if (logger.isDebugEnabled()) {
0984: logger.debug("changed state of " + proc.name() + "/"
0985: + proc.key() + " from " + requestedAsapState
0986: + " to " + newAsapState);
0987: }
0988: }
0989:
0990: /**
0991: * Terminates the process instance.
0992: *
0993: * <p>
0994: * The specification is inconsistent at this point. The WfXML 20
0995: * specification redirects to the ASAP specification where terminate should
0996: * be defined. This specification in the ASAP documentation is missing
0997: * (in the xsd as well). So we leave it unimplemented at this point.
0998: * If the implementation is more clear, this is the point where the
0999: * implementation must go.
1000: * </p>
1001: *
1002: * @param reqMsg the request message.
1003: * @param respMsg the response message
1004: * @throws SOAPException
1005: * error evaluating the request or constructing the response.
1006: * @throws RemoteException
1007: * error accessing the workflow engine.
1008: */
1009: private void terminate(SOAPMessage reqMsg, SOAPMessage respMsg)
1010: throws SOAPException, RemoteException {
1011: FaultUtils
1012: .setFault(
1013: respMsg,
1014: ASAPException.ASAP_OPERATION_FAILED,
1015: Consts.TERMINATE_REQUEST
1016: + " is not implemented due to a missing specification!");
1017: }
1018:
1019: /**
1020: * Gets the process manager that that can handle the package id, the
1021: * process id, and the process key that are encoded in the given uri.
1022: * @param uri the uri with package id, process id and process key.
1023: * @return process.
1024: * @throws RemoteException
1025: * error accessing the workflow engine
1026: * @throws InvalidKeyException
1027: * process manager or process does not exist
1028: */
1029: private WfProcess getProcess(String uri) throws RemoteException,
1030: InvalidKeyException {
1031: String packageId = getResourceReference().getPackageId();
1032: String processId = getResourceReference().getProcessId();
1033: String processKey = getResourceReference().getProcessKey();
1034:
1035: if (logger.isDebugEnabled()) {
1036: logger.debug("finding process definition'" + packageId
1037: + "/" + processId + "/" + processKey + "'...");
1038: }
1039:
1040: ProcessDirectory pd = getWorkflowService().processDirectory();
1041:
1042: WfProcess proc = pd.lookupProcess(packageId + "/" + processId,
1043: processKey);
1044:
1045: return proc;
1046: }
1047:
1048: /**
1049: * Creates a state changed notification message for the given observer.
1050: * @param observer the observer
1051: * @param newState the new state of the process
1052: * @param oldState the old state of the process
1053: * @return notification message.
1054: * @throws SOAPException
1055: * error creating the message.
1056: */
1057: public SOAPMessage createStateChangedMessage(String observer,
1058: String newState, String oldState) throws SOAPException {
1059: MessageFactory messageFactory = MessageFactory.newInstance();
1060: SOAPMessage message = messageFactory.createMessage();
1061:
1062: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
1063: SOAPHeader header = env.getHeader();
1064: SOAPHeaderElement req = header.addHeaderElement(env.createName(
1065: "Request", "as", Consts.ASAP_NS));
1066: SOAPElement receiverkey = req.addChildElement("ReceiverKey",
1067: Consts.ASAP_PREFIX);
1068: receiverkey.addTextNode(observer);
1069: SOAPElement senderkey = req.addChildElement("SenderKey",
1070: Consts.ASAP_PREFIX);
1071: senderkey.addTextNode(getResourceReference().getResourceKey());
1072: SOAPElement responseRequired = req.addChildElement(
1073: "ResponseRequired", Consts.ASAP_PREFIX);
1074: responseRequired.addTextNode("No");
1075:
1076: SOAPBody body = env.getBody();
1077: Name respName = env.createName(Consts.STATE_CHANGE_REQUEST,
1078: Consts.ASAP_PREFIX, Consts.ASAP_NS);
1079:
1080: SOAPBodyElement action = body.addBodyElement(respName);
1081: SOAPElement newStateNode = action.addChildElement("State",
1082: Consts.ASAP_PREFIX);
1083: newStateNode.addTextNode(newState);
1084: SOAPElement oldStateNode = action.addChildElement(
1085: "PreviousState", Consts.ASAP_PREFIX);
1086: oldStateNode.addTextNode(oldState);
1087:
1088: return message;
1089: }
1090:
1091: /**
1092: * Creates a completed notification message for a given observer.
1093: * @param observer the observer
1094: * @return notification message.
1095: * @throws SOAPException
1096: * error creating the message.
1097: * @throws SAXException
1098: * @throws TransformerException
1099: * @throws ResultNotAvailableException
1100: * @throws RemoteException
1101: */
1102: public SOAPMessage createCompletedMessage(String observer,
1103: ProcessData result) throws SOAPException, RemoteException {
1104: MessageFactory messageFactory = MessageFactory.newInstance();
1105: SOAPMessage message = messageFactory.createMessage();
1106:
1107: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
1108: SOAPHeader header = env.getHeader();
1109: SOAPHeaderElement req = header.addHeaderElement(env.createName(
1110: "Request", "as", Consts.ASAP_NS));
1111: SOAPElement receiverKey = req.addChildElement("ReceiverKey",
1112: Consts.ASAP_PREFIX);
1113: receiverKey.addTextNode(observer);
1114: SOAPElement senderKey = req.addChildElement("SenderKey",
1115: Consts.ASAP_PREFIX);
1116: senderKey.addTextNode(getResourceReference().getResourceKey());
1117: SOAPElement responseRequired = req.addChildElement(
1118: "ResponseRequired", Consts.ASAP_PREFIX);
1119: responseRequired.addTextNode("No");
1120:
1121: SOAPBody body = env.getBody();
1122: Name respName = env.createName(Consts.COMPLETED_REQUEST,
1123: Consts.ASAP_PREFIX, Consts.ASAP_NS);
1124:
1125: SOAPBodyElement action = body.addBodyElement(respName);
1126: appendResultData(message, result, action);
1127:
1128: return message;
1129: }
1130:
1131: /**
1132: * Appends the result data to the given node in a properties request.
1133: * @param respMsg the message to send
1134: * @param result result of the process.
1135: * @param parent the parent node
1136: * @throws RemoteException
1137: * Error accessing the workflow engine
1138: * @throws SOAPException
1139: * Error creating the message
1140: * @throws ResultNotAvailableException
1141: * @throws SAXException
1142: * @throws TransformerException
1143: */
1144: private void appendResultData(SOAPMessage respMsg,
1145: ProcessData result, SOAPBodyElement parent)
1146: throws RemoteException, SOAPException {
1147: SOAPElement resultData = parent.addChildElement("ResultData",
1148: Consts.ASAP_PREFIX);
1149:
1150: if (result == null) {
1151: return;
1152: }
1153:
1154: Iterator iterator = result.keySet().iterator();
1155: while (iterator.hasNext()) {
1156: String name = (String) iterator.next();
1157: Object value = result.get(name);
1158: SOAPElement dataNode = resultData.addChildElement(name,
1159: Consts.RS_PREFIX);
1160: maybeAddObjectAsTextNode(respMsg, value, dataNode);
1161: }
1162: }
1163:
1164: }
|