/*
* 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: ServiceRegistryResponseGenerator.java 2331 2007-03-29 11:46:54Z schnelle $
*
* $Log$
* Revision 1.9 2007/03/01 12:32:58 schnelle
* Enhanced Instance.SetProperties to process ContextData.
*
* Revision 1.8 2007/02/28 13:19:36 drmlipp
* Fixed charset problem.
*
* Revision 1.7 2007/02/16 21:00:21 mlipp
* Fixed some null pointer problems.
*
* Revision 1.6 2007/02/06 08:35:34 schnelle
* Started automatic generation of wsdl description.
*
* Revision 1.5 2007/02/01 13:44:44 schnelle
* Using namespace for factory schemas that do not contain '&'.
*
* Revision 1.4 2007/01/31 22:55:36 mlipp
* Some more refactoring and fixes of problems introduced by refactoring.
*
* Revision 1.3 2007/01/31 12:24:06 drmlipp
* Design revisited.
*
* Revision 1.2 2007/01/30 11:56:14 drmlipp
* Merged Wf-XML branch.
*
* Revision 1.1.2.21 2007/01/29 15:04:21 schnelle
* Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
*
* Revision 1.1.2.20 2007/01/29 13:40:32 schnelle
* Storing of the sender base in the servlet context.
*
* Revision 1.1.2.19 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.18 2007/01/16 11:05:42 schnelle
* Refactoring: Moved subscription handling methods to own class.
*
* Revision 1.1.2.17 2007/01/10 09:03:43 schnelle
* Implemented set properties methods.
*
* Revision 1.1.2.16 2006/12/20 13:32:25 schnelle
* Basic implementato of GetProperties for Instance and Activity.
*
* Revision 1.1.2.15 2006/12/19 14:43:29 schnelle
* Implementation of GetProperties for ServiceRegistry and Factory.
*
* Revision 1.1.2.14 2006/12/18 14:41:03 schnelle
* Preparatation for individual schema definition for each getproperties request.
*
* Revision 1.1.2.13 2006/12/18 11:56:51 schnelle
* Returning the XPDL node after a NewDefiniton to the ServiceRegistry.
*
* Revision 1.1.2.12 2006/12/14 08:50:21 schnelle
* Implemented CompleteActivity.
*
* Revision 1.1.2.11 2006/12/13 11:23:48 schnelle
* Implemented instance ListActivities.
*
* Revision 1.1.2.10 2006/12/12 13:24:38 schnelle
* Introduction of ASAPException to provide a detailed mesage.
*
* Revision 1.1.2.9 2006/12/12 09:34:35 schnelle
* Implemented ChangeState for Instance.
*
* Revision 1.1.2.8 2006/12/11 11:05:34 schnelle
* Added template methods for all requests.
*
* Revision 1.1.2.7 2006/12/01 12:49:54 schnelle
* Basic import of context data for process creation.
*
* Revision 1.1.2.6 2006/11/30 12:45:08 schnelle
* Basic implementation of Factory CreateInstance.
*
* Revision 1.1.2.5 2006/11/30 10:38:08 schnelle
* Implementation of Factory ListInstance.
*
* Revision 1.1.2.4 2006/11/29 14:12:37 schnelle
* Take respect to namespaces of asap requests and responses.
*
* Revision 1.1.2.3 2006/11/29 11:05:22 schnelle
* Full implementation of the request and response headers.
*
* Revision 1.1.2.2 2006/11/28 15:31:51 schnelle
* Proper selection of the response generator.
*
* Revision 1.1.2.1 2006/11/28 12:20:09 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.io.UnsupportedEncodingException;
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.Iterator;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.TransformerException;
import de.danet.an.workflow.api.ImportException;
import de.danet.an.workflow.api.InvalidKeyException;
import de.danet.an.workflow.api.ProcessDefinition;
import de.danet.an.workflow.api.ProcessDefinitionDirectory;
import de.danet.an.workflow.api.WorkflowService;
/**
* This class provides the methods of an {@link AbstractResponseGenerator}
* that are relevant for the service registry.
*
* <p>
* A <em>Service Registry</em> is a special purpose factory (or meta-factory)
* that can start new factories associated with new process definitions given to
* them. It can provide a list of factories, in the same way that a factory can
* provide a list of instances. This special purpose factory is needed to allow
* process definition tools to add new processes to the server.
* </p>
*
* @author Dirk Schnelle
*
*/
class ServiceRegistryResponseGenerator extends AbstractResponseGenerator {
/** Logger instance. */
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog
(ServiceRegistryResponseGenerator.class);
/**
* Constructs a new object.
*
* @param observerRegistry the observer registry.
* @param wfs Reference to the workflow engine.
* @param decoder the URI decoder.
*/
public ServiceRegistryResponseGenerator(ObserverRegistry observerRegistry,
WorkflowService wfs, ResourceReference decoder){
super(observerRegistry, wfs, decoder);
}
/* (non-Javadoc)
* @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator#evaluate(javax.xml.soap.SOAPMessage, javax.xml.soap.SOAPMessage)
*/
public void evaluate(SOAPMessage reqMsg, SOAPMessage respMsg)
throws SOAPException, RemoteException {
SOAPBodyElement actionElement = getActionElement(reqMsg);
String actName = actionElement.getElementName().getLocalName();
if (actName.equals (Consts.LIST_DEFINITIONS_REQUEST)) {
listDefinitions (reqMsg, respMsg);
} else if (actName.equals (Consts.NEW_DEFINITION_REQUEST)) {
newDefinition (actionElement, reqMsg, respMsg);
} else if (actName.equals (Consts.GET_PROPERTIES_REQUEST)) {
getServiceProperties (reqMsg, respMsg);
} else if (actName.equals (Consts.GET_PROPERTIES_REQUEST)) {
setServiceProperties (reqMsg, respMsg);
} else {
if (logger.isDebugEnabled()) {
logger.debug("unknown action '" + actName + "'");
}
FaultUtils.setFault(respMsg,
ASAPException.ASAP_INVALID_OPERATION_SPECIFICATION,
getResourceName() + ": Unknown action \"" + actName
+ "\".");
}
}
/**
* Creates a new definition.
*
* <p>
* Note that this implementation differs from the specification. An
* error is not thrown, if the factory exists. If the factory exists,
* the process definition will be overwritten without warning.
* </p>
*
* @param action the request 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 newDefinition(SOAPBodyElement action,
SOAPMessage reqMsg, SOAPMessage respMsg)
throws SOAPException, RemoteException{
String language = getChildsTextContent(action, "ProcessLanguage");
if ((language != null) && !language.equalsIgnoreCase("XPDL")) {
FaultUtils.setFault(respMsg,
ASAPException.ASAP_INVALID_CONTEXT_DATA,
"Allowed value for process language is XPDL only!");
return;
}
SOAPElement definition = findChildNode(action, "Definition");
SOAPElement pkg = findChildNode(definition, "Package");
try {
byte[] xpdlBytes = nodeToBytes(pkg);
if (logger.isDebugEnabled()) {
logger.debug("importing xpdl");
try {
logger.debug(new String(xpdlBytes, "UTF-8"));
} catch (UnsupportedEncodingException e) {
logger.debug(new String(xpdlBytes));
}
}
ProcessDefinitionDirectory pdd
= getWorkflowService().processDefinitionDirectory();
pdd.importProcessDefinitions(xpdlBytes);
} catch (TransformerException e) {
FaultUtils.setFault(respMsg,
ASAPException.ASAP_INVALID_CONTEXT_DATA,
"unable to transform xpdl");
return;
} catch (ImportException e) {
FaultUtils.setFault(respMsg, ASAPException.ASAP_OPERATION_FAILED,
e.getMessage());
return;
}
SOAPBodyElement defsNode = createWfxmlResponseNode(respMsg,
Consts.NEW_DEFINITION_RESPONSE);
defsNode.addNamespaceDeclaration("",
"http://www.wfmc.org/2002/XPDL1.0");
Iterator prefixes = pkg.getNamespacePrefixes();
while (prefixes.hasNext()) {
String prefix = (String) prefixes.next();
String uri = pkg.getNamespaceURI(prefix);
defsNode.addNamespaceDeclaration(prefix, uri);
}
importAsChild(defsNode, pkg);
}
/**
* Sets properties.
* @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 setServiceProperties(SOAPMessage reqMsg, SOAPMessage respMsg)
throws SOAPException, RemoteException {
if (logger.isDebugEnabled()) {
logger.debug("set service properties...");
}
String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
SOAPBodyElement propsNode
= createWfxmlResponseNode(respMsg, Consts.SET_PROPERTIES_RESPONSE);
appendServiceProperties(receiverKey, propsNode);
}
/**
* Creates a response that contains properties of the service registry.
*
* <p>
* This method produces XML that fails validation, since the WFXML
* schema definition defines a group to return theses properties, but
* which is never referenced, so that it must not be a part of
* a <code>GetpropertiesRs</code> message.
* </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 getServiceProperties(SOAPMessage reqMsg, SOAPMessage respMsg)
throws SOAPException, RemoteException {
if (logger.isDebugEnabled()) {
logger.debug("get service properties...");
}
String receiverKey = getHeaderValue(reqMsg, Consts.RECEIVER_KEY);
SOAPBodyElement propsNode
= createWfxmlResponseNode(respMsg, Consts.GET_PROPERTIES_RESPONSE);
appendServiceProperties(receiverKey, propsNode);
}
/**
* Reads the properties from the process definition and appends them to the
* parent.
* @param receiverKey this activity instance
* @param propsNode the parent node of the response message.
* @throws SOAPException
* error appending to the parent node
* @throws RemoteException
* error accessing the process
*/
private void appendServiceProperties(String receiverKey,
SOAPBodyElement propsNode) throws SOAPException {
SOAPElement keyNode
= propsNode.addChildElement("Key", Consts.WFXML_PREFIX);
keyNode.addTextNode(getResourceReference().getResourceKey());
SOAPElement nameNode
= propsNode.addChildElement("Name", Consts.WFXML_PREFIX);
nameNode.addTextNode("WfMOpen ServiceRegistry");
SOAPElement descNode
= propsNode.addChildElement("Description", Consts.WFXML_PREFIX);
descNode.addTextNode("The WfMOpen ServiceRegistry");
SOAPElement version
= propsNode.addChildElement("Version", Consts.WFXML_PREFIX);
version.addTextNode("0.1");
SOAPElement state
= propsNode.addChildElement("Status", Consts.WFXML_PREFIX);
state.addTextNode("Enabled");
}
/**
* Creates a response that contains the known definitions.
* @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 listDefinitions(SOAPMessage reqMsg, SOAPMessage respMsg)
throws SOAPException, RemoteException {
if (logger.isDebugEnabled()) {
logger.debug("list all process definitions...");
}
SOAPBodyElement defsNode = createWfxmlResponseNode(respMsg,
Consts.LIST_DEFINITIONS_RESPONSE);
ProcessDefinitionDirectory pdd
= getWorkflowService().processDefinitionDirectory();
Collection procdefs = pdd.processDefinitions();
for (Iterator i = procdefs.iterator(); i.hasNext ();) {
ProcessDefinition pd = (ProcessDefinition)i.next ();
try {
if (!pdd.isEnabled(pd.packageId(), pd.processId())) {
continue;
}
} catch (InvalidKeyException e) {
continue;
}
SOAPElement defInfo = defsNode.addChildElement
("DefinitionInfo", Consts.WFXML_PREFIX);
SOAPElement defKey
= defInfo.addChildElement("DefinitionKey", Consts.WFXML_PREFIX);
ResourceReference procDefRes = new ResourceReference
(getResourceReference().getBaseUrl(), pd);
defKey.addTextNode(procDefRes.getResourceKey());
SOAPElement name = defInfo.addChildElement("Name",
Consts.WFXML_PREFIX);
String val = pd.processName();
maybeAddTextNode(name, val);
SOAPElement descr = defInfo.addChildElement("Description",
Consts.WFXML_PREFIX);
val = pd.processHeader().description();
maybeAddTextNode(descr, val);
SOAPElement version = defInfo.addChildElement("Version",
Consts.WFXML_PREFIX);
val = pd.processHeader().version();
maybeAddTextNode(version, val);
SOAPElement status = defInfo.addChildElement("Status",
Consts.WFXML_PREFIX);
val = pd.processHeader().publicationStatus();
maybeAddTextNode(status, val);
}
}
/* (non-Javadoc)
* @see de.danet.an.workflow.clients.wfxml.AbstractResponseGenerator#getSender()
*/
protected String getResourceName() {
return RESOURCE_SERVICE_REGISTRY;
}
}