0001: /*
0002: * This file is part of the WfXML servlet.
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: Servlet.java,v 1.25 2007/07/15 22:01:11 mlipp Exp $
0021: *
0022: * $Log: Servlet.java,v $
0023: * Revision 1.25 2007/07/15 22:01:11 mlipp
0024: * Fixed thread not running problem.
0025: *
0026: * Revision 1.24 2007/04/03 14:47:54 schnelle
0027: * Some code cleanup.
0028: *
0029: * Revision 1.23 2007/03/29 11:46:54 schnelle
0030: * Reactivated ASAPException to propagate ASAP error messages in cases of an invalid key, a missing resource or an invalid factory.
0031: *
0032: * Revision 1.22 2007/03/27 21:59:42 mlipp
0033: * Fixed lots of checkstyle warnings.
0034: *
0035: * Revision 1.21 2007/03/01 12:32:57 schnelle
0036: * Enhanced Instance.SetProperties to process ContextData.
0037: *
0038: * Revision 1.20 2007/02/21 21:32:29 mlipp
0039: * Using pooled JMS connections when in EJB now.
0040: *
0041: * Revision 1.19 2007/02/17 21:19:48 mlipp
0042: * Workflow service caching redone.
0043: *
0044: * Revision 1.18 2007/02/06 08:35:34 schnelle
0045: * Started automatic generation of wsdl description.
0046: *
0047: * Revision 1.17 2007/02/01 10:08:36 drmlipp
0048: * Removed no longer used observer resource.
0049: *
0050: * Revision 1.16 2007/01/31 22:55:36 mlipp
0051: * Some more refactoring and fixes of problems introduced by refactoring.
0052: *
0053: * Revision 1.15 2007/01/31 14:53:06 schnelle
0054: * Small corrections wvaluating the resource reference.
0055: *
0056: * Revision 1.14 2007/01/31 12:24:06 drmlipp
0057: * Design revisited.
0058: *
0059: * Revision 1.13 2007/01/30 11:56:14 drmlipp
0060: * Merged Wf-XML branch.
0061: *
0062: * Revision 1.12.6.24 2007/01/29 15:04:19 schnelle
0063: * Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
0064: *
0065: * Revision 1.12.6.23 2007/01/29 13:40:31 schnelle
0066: * Storing of the sender base in the servlet context.
0067: *
0068: * Revision 1.12.6.22 2007/01/29 10:48:28 schnelle
0069: * Using a key-value encoding of the parameters instead of the directory approach.
0070: *
0071: * Revision 1.12.6.21 2007/01/26 15:50:28 schnelle
0072: * Added encoding for process id and package id.
0073: *
0074: * Revision 1.12.6.20 2007/01/24 14:22:38 schnelle
0075: * Observer handler starts on servlet startup.
0076: *
0077: * Revision 1.12.6.19 2007/01/24 10:56:50 schnelle
0078: * Prepared return of a result for aobservers.
0079: *
0080: * Revision 1.12.6.18 2007/01/19 12:34:56 schnelle
0081: * Moved generation and decoding of the URI that is used as the receiver key to new class URIDecoder.
0082: *
0083: * Revision 1.12.6.17 2007/01/19 07:59:35 schnelle
0084: * Corrected return value for factory list instances.
0085: *
0086: * Revision 1.12.6.16 2007/01/16 11:05:42 schnelle
0087: * Refactoring: Moved subscription handling methods to own class.
0088: *
0089: * Revision 1.12.6.15 2007/01/11 10:23:52 schnelle
0090: * Creation of StateChanged notifications.
0091: *
0092: * Revision 1.12.6.14 2007/01/10 13:41:27 schnelle
0093: * Implemented subscribe.
0094: *
0095: * Revision 1.12.6.13 2007/01/09 12:06:14 schnelle
0096: * Implemented methods to receive xsd and wsdl files.
0097: *
0098: * Revision 1.12.6.12 2006/12/20 13:32:24 schnelle
0099: * Basic implementato of GetProperties for Instance and Activity.
0100: *
0101: * Revision 1.12.6.11 2006/12/18 14:41:02 schnelle
0102: * Preparatation for individual schema definition for each getproperties request.
0103: *
0104: * Revision 1.12.6.10 2006/12/13 11:23:48 schnelle
0105: * Implemented instance ListActivities.
0106: *
0107: * Revision 1.12.6.9 2006/12/12 13:24:38 schnelle
0108: * Introduction of ASAPException to provide a detailed mesage.
0109: *
0110: * Revision 1.12.6.8 2006/12/12 09:34:35 schnelle
0111: * Implemented ChangeState for Instance.
0112: *
0113: * Revision 1.12.6.7 2006/12/11 11:05:34 schnelle
0114: * Added template methods for all requests.
0115: *
0116: * Revision 1.12.6.6 2006/12/01 12:49:54 schnelle
0117: * Basic import of context data for process creation.
0118: *
0119: * Revision 1.12.6.5 2006/11/29 14:12:37 schnelle
0120: * Take respect to namespaces of asap requests and responses.
0121: *
0122: * Revision 1.12.6.4 2006/11/28 15:31:51 schnelle
0123: * Proper selection of the response generator.
0124: *
0125: * Revision 1.12.6.3 2006/11/28 12:20:09 schnelle
0126: * Creation of a separate class to handle the issues for a specific resource.
0127: *
0128: * Revision 1.12.6.2 2006/11/27 15:41:55 schnelle
0129: * Introducing some constants for request and response identification.
0130: *
0131: * Revision 1.12.6.1 2006/11/24 12:19:13 schnelle
0132: * Separtion of response generation into ResponseGenerator class.
0133: *
0134: * Revision 1.12 2006/09/29 12:32:11 drmlipp
0135: * Consistently using WfMOpen as projct name now.
0136: *
0137: * Revision 1.11 2005/11/17 21:30:25 mlipp
0138: * Finished re-organization of jetspeed module creation.
0139: *
0140: * Revision 1.10 2005/06/18 19:43:12 mlipp
0141: * Improved.
0142: *
0143: * Revision 1.9 2005/06/01 20:46:16 mlipp
0144: * Started getProperties.
0145: *
0146: * Revision 1.8 2005/04/24 18:55:34 mlipp
0147: * Fixed bug with scalar result.
0148: *
0149: * Revision 1.7 2005/04/11 20:22:18 mlipp
0150: * Implemented enabled process definition list.
0151: *
0152: * Revision 1.6 2005/04/10 20:59:30 mlipp
0153: * Improved exception handling.
0154: *
0155: * Revision 1.5 2005/04/10 19:53:54 mlipp
0156: * Valid response and JaWE workaround.
0157: *
0158: * Revision 1.4 2005/04/06 21:08:05 mlipp
0159: * Getting on...
0160: *
0161: * Revision 1.3 2005/04/06 20:03:26 mlipp
0162: * Generating web.xml now.
0163: *
0164: * Revision 1.2 2005/01/24 20:25:56 mlipp
0165: * Reverted saaj back to 1.1 to fit Axis version.
0166: *
0167: * Revision 1.1 2005/01/23 15:23:11 mlipp
0168: * Getting started with WfXML.
0169: *
0170: */
0171: package de.danet.an.workflow.clients.wfxml;
0172:
0173: import java.io.File;
0174: import java.io.FileInputStream;
0175: import java.io.FileNotFoundException;
0176: import java.io.IOException;
0177: import java.io.OutputStream;
0178: import java.rmi.RemoteException;
0179: import java.util.Enumeration;
0180: import java.util.Iterator;
0181: import java.util.List;
0182: import java.util.Map;
0183: import java.util.StringTokenizer;
0184:
0185: import javax.naming.NamingException;
0186: import javax.naming.directory.InvalidSearchFilterException;
0187: import javax.security.auth.callback.Callback;
0188: import javax.security.auth.callback.CallbackHandler;
0189: import javax.security.auth.callback.NameCallback;
0190: import javax.security.auth.callback.PasswordCallback;
0191: import javax.security.auth.callback.TextOutputCallback;
0192: import javax.security.auth.callback.UnsupportedCallbackException;
0193: import javax.security.auth.login.LoginContext;
0194: import javax.security.auth.login.LoginException;
0195: import javax.servlet.ServletConfig;
0196: import javax.servlet.ServletContext;
0197: import javax.servlet.ServletException;
0198: import javax.servlet.ServletOutputStream;
0199: import javax.servlet.http.HttpServlet;
0200: import javax.servlet.http.HttpServletRequest;
0201: import javax.servlet.http.HttpServletResponse;
0202: import javax.servlet.http.HttpSession;
0203: import javax.wsdl.Binding;
0204: import javax.wsdl.Definition;
0205: import javax.wsdl.Import;
0206: import javax.wsdl.Port;
0207: import javax.wsdl.Service;
0208: import javax.wsdl.Types;
0209: import javax.wsdl.WSDLException;
0210: import javax.wsdl.extensions.schema.Schema;
0211: import javax.wsdl.extensions.schema.SchemaImport;
0212: import javax.wsdl.extensions.soap.SOAPAddress;
0213: import javax.wsdl.factory.WSDLFactory;
0214: import javax.wsdl.xml.WSDLReader;
0215: import javax.wsdl.xml.WSDLWriter;
0216: import javax.xml.namespace.QName;
0217: import javax.xml.soap.MessageFactory;
0218: import javax.xml.soap.MimeHeaders;
0219: import javax.xml.soap.Name;
0220: import javax.xml.soap.Node;
0221: import javax.xml.soap.SOAPElement;
0222: import javax.xml.soap.SOAPException;
0223: import javax.xml.soap.SOAPFault;
0224: import javax.xml.soap.SOAPHeader;
0225: import javax.xml.soap.SOAPHeaderElement;
0226: import javax.xml.soap.SOAPMessage;
0227: import javax.xml.transform.Transformer;
0228: import javax.xml.transform.TransformerConfigurationException;
0229: import javax.xml.transform.TransformerException;
0230: import javax.xml.transform.TransformerFactory;
0231: import javax.xml.transform.dom.DOMSource;
0232: import javax.xml.transform.stream.StreamResult;
0233:
0234: import com.ibm.wsdl.extensions.soap.SOAPAddressImpl;
0235: import com.ibm.wsdl.factory.WSDLFactoryImpl;
0236:
0237: import de.danet.an.util.XMLUtil;
0238: import de.danet.an.workflow.api.EventSubscriber;
0239: import de.danet.an.workflow.api.FactoryConfigurationError;
0240: import de.danet.an.workflow.api.WorkflowService;
0241: import de.danet.an.workflow.api.WorkflowServiceFactory;
0242: import de.danet.an.workflow.omgcore.WfAuditEvent;
0243:
0244: /**
0245: * This class provides an interface between the workflow engine (i.e. its
0246: * processes and any HTTP based client (e.g. Web browser application).
0247: *
0248: * <p>
0249: * The servlet works as a mediator for services of the different roles,
0250: * excluding the observer, as they are defined by the Wf-XML 2.0 standard.
0251: * </p>
0252: *
0253: * @author Michael Lipp
0254: * @author Dirk Schnelle
0255: * @version $Revision: 1.25 $
0256: * @web.servlet name="WfXML Servlet" display-name="WfXML Servlet"
0257: * description="WfXML interface servlet" load-on-startup="1"
0258: * @web.servlet-mapping url-pattern="/*"
0259: * @web.servlet-init-param name="SenderBase" value="@@@_WfXML_service_url_@@@"
0260: * @web.servlet-init-param name="ApplicationPolicy"
0261: * value="@@@_WfXML_servlet_application_policy_@@@"
0262: * @web.servlet-init-param name="UserName"
0263: * value="@@@_WfXML_servlet_username_@@@"
0264: * @web.servlet-init-param name="Password"
0265: * value="@@@_WfXML_servlet_password_@@@"
0266: * @web.resource-ref name="jdbc/WfEngine" type="javax.sql.DataSource"
0267: * auth="Container"
0268: * @jboss.resource-ref res-ref-name="jdbc/WfEngine" jndi-name="java:/WfMOpenDS"
0269: */
0270: public class Servlet extends HttpServlet {
0271: /** Logger instance. */
0272: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
0273: .getLog(Servlet.class);
0274:
0275: /** Attribute sender base in the servlet context. */
0276: private static final String SENDER_BASE = "SenderBase";
0277:
0278: /** Attribute observer registry in the servlet context. */
0279: static final String OBSERVER_REGISTRY = "ObserverRegistry";
0280:
0281: /** The servlet context. */
0282: private ServletContext ctx = null;
0283:
0284: /** Audit handler thread. */
0285: private AuditHandlerThread auditHandlerThread = null;
0286:
0287: /** Cache of the last workflow service lookup. */
0288: private WorkflowService wfsCache = null;
0289:
0290: /** Base URL that is used as a prefix in the sender key. */
0291: private String senderBase;
0292:
0293: /** The observer registry. */
0294: private ObserverRegistry observerRegistry = null;
0295:
0296: /** The event subscriber */
0297: private EventSubscriber eventSubscriber = null;
0298:
0299: /**
0300: * Request workflow service.
0301: */
0302: private synchronized WorkflowService getWorkflowService() {
0303: synchronized (this ) {
0304: if (wfsCache == null) {
0305: WorkflowServiceFactory factory = WorkflowServiceFactory
0306: .newInstance();
0307: wfsCache = factory.newWorkflowService();
0308: }
0309: return wfsCache;
0310: }
0311: }
0312:
0313: /**
0314: * Default initialization method.
0315: *
0316: * @param servletConfig
0317: * a <code>ServletConfig</code> value
0318: * @exception ServletException
0319: * if an error occurs
0320: */
0321: public void init(ServletConfig servletConfig)
0322: throws ServletException {
0323: ctx = servletConfig.getServletContext();
0324:
0325: try {
0326: observerRegistry = new ObserverRegistry();
0327: ctx.setAttribute(OBSERVER_REGISTRY, observerRegistry);
0328: } catch (NamingException e) {
0329: throw new ServletException(e);
0330: }
0331:
0332: // Read configuration values and overwrite default, if set
0333: if (servletConfig != null) {
0334: senderBase = servletConfig.getInitParameter(SENDER_BASE);
0335: String applicationPolicy = servletConfig
0336: .getInitParameter("ApplicationPolicy");
0337: String userName = servletConfig
0338: .getInitParameter("UserName");
0339: String password = servletConfig
0340: .getInitParameter("Password");
0341: // The workflow engine may not yet be available, when the servlet
0342: // is started. Repeat trying to subscribe until we are successful.
0343: auditHandlerThread = new AuditHandlerThread(
0344: applicationPolicy, userName, password);
0345: auditHandlerThread.start();
0346: }
0347: }
0348:
0349: /*
0350: * (non-Javadoc)
0351: *
0352: * @see javax.servlet.GenericServlet#destroy()
0353: */
0354: public void destroy() {
0355: if (auditHandlerThread != null) {
0356: auditHandlerThread.terminate();
0357: try {
0358: auditHandlerThread.join();
0359: } catch (InterruptedException e) {
0360: // deliberately ignored
0361: }
0362: auditHandlerThread = null;
0363: }
0364: if (eventSubscriber != null) {
0365: wfsCache.release(eventSubscriber);
0366: eventSubscriber = null;
0367: }
0368: if (wfsCache != null) {
0369: wfsCache.release(wfsCache);
0370: wfsCache = null;
0371: }
0372: super .destroy();
0373: }
0374:
0375: /**
0376: * Retrieves the sender base.
0377: *
0378: * <p>
0379: * The sender base is derived from the request. The this value may be
0380: * overridden via the <code>SenderBase</code> parameter in the
0381: * <code>web.xml</code>.
0382: * </p>
0383: * @param request the servlet request.
0384: * @return the sender base.
0385: */
0386: String getSenderBase(HttpServletRequest request) {
0387: if (senderBase != null && senderBase.length() > 0) {
0388: return senderBase;
0389: }
0390: return getRequestBasePath(request);
0391: }
0392:
0393: /**
0394: * Create a new process and forward initial response (e.g. HTML start page).
0395: * See user manual (chapter "tools") for a detailed description.
0396: *
0397: * @param request
0398: * a <code>HttpServletRequest</code> value
0399: * @param response
0400: * a <code>HttpServletResponse</code> value
0401: * @exception ServletException
0402: * if an error occurs
0403: * @exception IOException
0404: * if an error occurs
0405: */
0406: public void doGet(HttpServletRequest request,
0407: HttpServletResponse response) throws ServletException,
0408: IOException {
0409:
0410: String requestUrl = request.getRequestURL().toString();
0411: String query = request.getQueryString();
0412: if ((query != null) && query.equalsIgnoreCase("wsdl")) {
0413: getWsdl(request, response);
0414: } else if (requestUrl.endsWith(".xsd")
0415: || requestUrl.endsWith(".wsdl")) {
0416: getFile(request, response);
0417: } else {
0418: doPost(request, response);
0419: }
0420: }
0421:
0422: /**
0423: * Returns the contents of the requested file encoded as
0424: * <code>text/plain</code>.
0425: *
0426: * @param request
0427: * @param response
0428: * @throws IOException
0429: * Error accessing the file.
0430: */
0431: private void getWsdl(HttpServletRequest request,
0432: HttpServletResponse response) throws IOException {
0433: WSDLFactory factory = new WSDLFactoryImpl();
0434: WSDLReader reader = factory.newWSDLReader();
0435: WSDLWriter writer = factory.newWSDLWriter();
0436:
0437: String wsdllocation = ctx.getRealPath("/wfxml.wsdl");
0438: File file = new File(wsdllocation);
0439:
0440: try {
0441: Definition definition = reader.readWSDL(file.toURI()
0442: .getPath());
0443:
0444: String requestBasePath = getRequestBasePath(request);
0445: if (requestBasePath.endsWith("/")) {
0446: requestBasePath = requestBasePath.substring(0,
0447: requestBasePath.length() - 1);
0448: }
0449:
0450: definition.setDocumentBaseURI(requestBasePath);
0451: adjustReferencedElements(definition, requestBasePath);
0452:
0453: addWsdlService(request, definition, "WfXmlServiceRegistry",
0454: "WfMOpenServiceRegistry",
0455: "WfMOpenServiceRegistryService");
0456:
0457: addWsdlService(request, definition, "WfXmlFactory",
0458: "WfMOpenFactory", "WfMOpenFactoryService");
0459:
0460: addWsdlService(request, definition, "WfXmlInstance",
0461: "WfMOpenInstance", "WfMOpenInstanceService");
0462:
0463: addWsdlService(request, definition, "WfXmlActivity",
0464: "WfMOpenActivity", "WfMOpenActivityService");
0465:
0466: writer.writeWSDL(definition, response.getOutputStream());
0467: } catch (WSDLException e) {
0468: throw new IOException(e.getMessage());
0469: }
0470: }
0471:
0472: /**
0473: * Adjust the import definitions to the path under which we were called.
0474: * This is necessary, since the referenced resources will be retrieved
0475: * from a package above the servlet directory, if the servlet
0476: * is called with <code>http://.../wfxml?wsdl</code>.
0477: * @param definition the WSDL definition.
0478: * @param requestBasePath the base path.
0479: */
0480: private void adjustReferencedElements(Definition definition,
0481: String requestBasePath) {
0482: List imports = definition
0483: .getImports("http://www.oasis-open.org/asap/0.9/asap.wsdl");
0484: Import asapImport = (Import) imports.get(0);
0485: asapImport.setLocationURI(requestBasePath + "/asap.wsdl");
0486:
0487: // TODO: The types are not properly written to the definition.
0488: Types types = definition.getTypes();
0489: List typeList = types.getExtensibilityElements();
0490: Iterator iterator = typeList.iterator();
0491: while (iterator.hasNext()) {
0492: Object o = iterator.next();
0493: if (o instanceof Schema) {
0494: Schema schema = (Schema) o;
0495: Map map = schema.getImports();
0496:
0497: List schemaImports = (List) map
0498: .get("http://www.wfmc.org/wfxml/2.0/wfxml20.xsd");
0499: SchemaImport schemaImport = (SchemaImport) schemaImports
0500: .get(0);
0501: Schema ref = schemaImport.getReferencedSchema();
0502:
0503: schemaImport.setSchemaLocationURI(requestBasePath
0504: + "/wfxml20.xsd");
0505: }
0506: }
0507: }
0508:
0509: /**
0510: * Adds a service description to the WSDL definition.
0511: * @param request
0512: * @param definition
0513: */
0514: private void addWsdlService(HttpServletRequest request,
0515: Definition definition, String bindingName, String portName,
0516: String serviceName) {
0517: Service service = definition.createService();
0518: Port port = definition.createPort();
0519: QName name = new QName(
0520: "http://wfmopen.sourceforge.net/wfxml20.wsdl",
0521: bindingName, "");
0522: Binding binding = definition.getBinding(name);
0523: port.setName(portName);
0524: port.setBinding(binding);
0525:
0526: service.setQName(new QName(
0527: "http://wfmopen.sourceforge.net/wfxml20.wsdl",
0528: serviceName, "tns"));
0529: service.addPort(port);
0530: SOAPAddress address = new SOAPAddressImpl();
0531: address.setLocationURI(getRequestBasePath(request));
0532:
0533: port.addExtensibilityElement(address);
0534:
0535: definition.addService(service);
0536: }
0537:
0538: /**
0539: * Returns the contents of the requested file encoded as
0540: * <code>text/plain</code>.
0541: *
0542: * @param request
0543: * @param response
0544: * @throws IOException
0545: * Error accessing the file.
0546: */
0547: private void getFile(HttpServletRequest request,
0548: HttpServletResponse response) throws IOException {
0549: ServletOutputStream out = response.getOutputStream();
0550: // Get the file to view
0551: String file = request.getPathTranslated();
0552:
0553: // No file, nothing to view
0554: if (file == null) {
0555: out.println("No file to view");
0556: return;
0557: }
0558:
0559: // Get and set the type of the file
0560: response.setContentType("text/plain");
0561:
0562: // Return the file
0563: try {
0564: FileInputStream fis = null;
0565: try {
0566: fis = new FileInputStream(file);
0567: byte[] buf = new byte[4 * 1024]; // 4K buffer
0568: int bytesRead;
0569: while ((bytesRead = fis.read(buf)) != -1) {
0570: out.write(buf, 0, bytesRead);
0571: }
0572: } finally {
0573: if (fis != null) {
0574: fis.close();
0575: }
0576: }
0577: } catch (FileNotFoundException e) {
0578: out.println("File not found");
0579: }
0580: }
0581:
0582: /**
0583: * Receive HTTP requests for a channel based access to a workflow process.
0584: * See user manual (chapter "tools") for a detailed description.
0585: *
0586: * @param request
0587: * a <code>HttpServletRequest</code> value
0588: * @param response
0589: * a <code>HttpServletResponse</code> value
0590: * @exception ServletException
0591: * if an error occurs
0592: * @exception IOException
0593: * if an error occurs
0594: */
0595: public void doPost(HttpServletRequest request,
0596: HttpServletResponse response) throws ServletException,
0597: IOException {
0598: HttpSession session = request.getSession(true);
0599:
0600: MessageFactory messageFactory = null;
0601: try {
0602: messageFactory = MessageFactory.newInstance();
0603: } catch (SOAPException e) {
0604: response.sendError(
0605: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0606: "Unable to access SOAP subsystem: "
0607: + e.getMessage());
0608: return;
0609: }
0610:
0611: SOAPMessage respMsg = null;
0612: WorkflowService wfs = null;
0613: // workaround JaWE bug
0614: boolean agentIsJaWE = false;
0615: Throwable fatalProblem = null;
0616:
0617: try {
0618: // get mime request headers for building request structure
0619: MimeHeaders mimeHeaders = new MimeHeaders();
0620: for (Enumeration en = request.getHeaderNames(); en
0621: .hasMoreElements();) {
0622: String headerName = (String) en.nextElement();
0623: String headerVal = request.getHeader(headerName);
0624: if (headerName.equalsIgnoreCase("user-agent")
0625: && headerVal.startsWith("JaWE")) {
0626: agentIsJaWE = true;
0627: }
0628: StringTokenizer tk = new StringTokenizer(headerVal, ",");
0629: while (tk.hasMoreTokens()) {
0630: mimeHeaders.addHeader(headerName, tk.nextToken()
0631: .trim());
0632: }
0633: }
0634:
0635: // build request message from headers and stream
0636: SOAPMessage reqMsg = messageFactory.createMessage(
0637: mimeHeaders, request.getInputStream());
0638:
0639: while (true) {
0640: try {
0641: respMsg = messageFactory.createMessage();
0642: wfs = getWorkflowService();
0643: if (wfs == null) {
0644: SOAPFault fault = respMsg.getSOAPPart()
0645: .getEnvelope().getBody().addFault();
0646: fault
0647: .setFaultString("Unable to access workflow service");
0648: return;
0649: }
0650:
0651: AbstractResponseGenerator respgen = getResponseGenerator(
0652: getSenderBase(request), reqMsg, wfs);
0653: respgen.fillResponseHeader(reqMsg, respMsg);
0654: respgen.evaluate(reqMsg, respMsg);
0655:
0656: break;
0657: } catch (RemoteException e) {
0658: logger.debug(
0659: "Problem while trying to create response "
0660: + "(retrying): " + e.getMessage(),
0661: e);
0662: }
0663: }
0664: } catch (SOAPException e) {
0665: fatalProblem = e;
0666: } catch (FactoryConfigurationError e) {
0667: fatalProblem = e;
0668: } catch (ASAPException e) {
0669: fatalProblem = e;
0670: } catch (InvalidSearchFilterException e) {
0671: fatalProblem = e;
0672: } finally {
0673: if (fatalProblem != null) {
0674: if (logger.isDebugEnabled()) {
0675: logger.debug("error executing the client request",
0676: fatalProblem);
0677: }
0678:
0679: try {
0680: respMsg = messageFactory.createMessage();
0681: if (fatalProblem instanceof ASAPException) {
0682: ASAPException cause = (ASAPException) fatalProblem;
0683: FaultUtils.setFault(respMsg, cause
0684: .getErrorCode(), cause.getMessage());
0685: } else {
0686: FaultUtils.setFault(respMsg, fatalProblem);
0687: }
0688: } catch (SOAPException ee) {
0689: response
0690: .sendError(
0691: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0692: "Unable to use SOAP subsystem: "
0693: + ee.getMessage());
0694: return;
0695: }
0696: }
0697: }
0698:
0699: sendResponse(response, respMsg, agentIsJaWE);
0700: }
0701:
0702: /**
0703: * Retrieves an {@link AbstractResponseGenerator} instance that is capable
0704: * to handle the current request.
0705: *
0706: * <p>
0707: * The response generator is determined through evaluation of the
0708: * <code><as:RecieverKey></code> in the header of the request
0709: * message
0710: * </p>
0711: *
0712: * @param senderBase the base for constructing the sender key
0713: * @param reqMsg
0714: * the SOAP request.
0715: * @param wfs
0716: * reference to the workflow engine.
0717: *
0718: * @return Response generator to handle the current request.
0719: * @throws SOAPException
0720: * error evaluating the SOAP header.
0721: * @throws FileNotFoundException
0722: * error obtaining the sender base.
0723: */
0724: private AbstractResponseGenerator getResponseGenerator(
0725: String senderBase, SOAPMessage reqMsg, WorkflowService wfs)
0726: throws SOAPException, ASAPException,
0727: InvalidSearchFilterException {
0728: // Determine the base request URL as a basis for referenced
0729: // resources.
0730: String receiverKey = null;
0731: receiverKey = getReceiverKey(reqMsg);
0732:
0733: ResourceReference resRef = new ResourceReference(senderBase,
0734: receiverKey);
0735:
0736: ObserverRegistry observerRegistry = (ObserverRegistry) ctx
0737: .getAttribute(OBSERVER_REGISTRY);
0738: String resource = resRef.getResource();
0739: if (resource
0740: .equals(AbstractResponseGenerator.RESOURCE_SERVICE_REGISTRY)) {
0741: return new ServiceRegistryResponseGenerator(
0742: observerRegistry, wfs, resRef);
0743: }
0744: if (resource.equals(AbstractResponseGenerator.RESOURCE_FACTORY)) {
0745: return new FactoryResponseGenerator(observerRegistry, wfs,
0746: resRef);
0747: }
0748: if (resource
0749: .equals(AbstractResponseGenerator.RESOURCE_INSTANCE)) {
0750: return new InstanceResponseGenerator(observerRegistry, wfs,
0751: resRef);
0752: }
0753: if (resource
0754: .equals(AbstractResponseGenerator.RESOURCE_ACTIVITY)) {
0755: return new ActivityResponseGenerator(observerRegistry, wfs,
0756: resRef);
0757: }
0758:
0759: // This should not happen, since we trap into an exception, trying
0760: // to obtain the resource string.
0761: throw new ASAPException(ASAPException.ASAP_INVALID_FACTORY,
0762: receiverKey + " cannot be mapped to a known resource.");
0763: }
0764:
0765: /**
0766: * Retrieves the base path that was used to call this servlet. Since this
0767: * servlet may be called with the same URL as it is specified in the
0768: * <code>SenderKey</code> of the ASAP header, we have to determine the
0769: * base path without any extra path info to create the responses.
0770: * @param request the request.
0771: * @return base path.
0772: */
0773: private String getRequestBasePath(HttpServletRequest request) {
0774: String requestUrl = request.getRequestURL().toString();
0775: String servletPath = request.getPathInfo();
0776:
0777: // Some implementations may return "/" if there is no extra path info
0778: // available, although they should return null in that case.
0779: if ((servletPath == null) || (servletPath.length() <= 1)) {
0780: return requestUrl;
0781: }
0782:
0783: return requestUrl.substring(0, requestUrl.length()
0784: - servletPath.length());
0785: }
0786:
0787: /**
0788: * Reads the <code>ReceiverKey</code> from the request header of the given
0789: * message.
0790: *
0791: * @param message
0792: * the message to inspect.
0793: * @return value of the <code>ReceiverKey</code>.
0794: * @throws SOAPException
0795: * error evaluating the message.
0796: */
0797: private String getReceiverKey(SOAPMessage message)
0798: throws SOAPException, ASAPException {
0799: SOAPHeaderElement headerElement = null;
0800:
0801: SOAPHeader header = message.getSOAPHeader();
0802: for (Iterator i = header.getChildElements(); i.hasNext();) {
0803: Node node = (Node) i.next();
0804: if (node instanceof SOAPHeaderElement) {
0805: headerElement = (SOAPHeaderElement) node;
0806:
0807: Name headerElementName = headerElement.getElementName();
0808: String localName = headerElementName.getLocalName();
0809: if (localName.equals(Consts.REQUEST_HEADER)) {
0810: String headerUri = headerElementName.getURI();
0811: if (headerUri.equals(Consts.ASAP_NS)
0812: || headerUri.equals(Consts.WFXML_NS)) {
0813: break;
0814: }
0815: }
0816: }
0817: }
0818:
0819: if (headerElement == null) {
0820: throw new ASAPException(ASAPException.ASAP_ELEMENT_MISSING,
0821: "ASAP request header not found.");
0822: }
0823:
0824: for (Iterator i = headerElement.getChildElements(); i.hasNext();) {
0825: Node node = (Node) i.next();
0826: if (node instanceof SOAPElement) {
0827: SOAPElement element = (SOAPElement) node;
0828: String name = element.getElementName().getLocalName();
0829: if (name.equals(Consts.RECEIVER_KEY)) {
0830: return XMLUtil.getFirstLevelTextContent(element);
0831: }
0832: }
0833: }
0834:
0835: throw new ASAPException(ASAPException.ASAP_ELEMENT_MISSING,
0836: Consts.SENDER_KEY + " must be specified in the header");
0837: }
0838:
0839: /**
0840: * Sends the response.
0841: *
0842: * @param response
0843: * the servlet's response.
0844: * @param respMsg
0845: * the SOAP response message.
0846: * @param agentIsJaWE
0847: * <code>true</code> if this servlet was called by JaWE.
0848: * @throws IOException
0849: * Error accessing the output stream.
0850: */
0851: private void sendResponse(HttpServletResponse response,
0852: SOAPMessage respMsg, boolean agentIsJaWE)
0853: throws IOException {
0854: response.setHeader("Content-Type", "text/xml");
0855: OutputStream os = response.getOutputStream();
0856:
0857: try {
0858: if (!agentIsJaWE) {
0859: respMsg.writeTo(os);
0860: } else {
0861: // workaround JaWE bug. JaWE cannot handle pretty
0862: // printed XML
0863: try {
0864: TransformerFactory tf = TransformerFactory
0865: .newInstance();
0866: Transformer t = tf.newTransformer();
0867: t.transform(new DOMSource(respMsg.getSOAPPart()
0868: .getEnvelope()), new StreamResult(os));
0869: } catch (TransformerConfigurationException ee) {
0870: response
0871: .sendError(
0872: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0873: "Cannot get transformer: "
0874: + ee.getMessage());
0875: return;
0876: } catch (TransformerException ee) {
0877: response
0878: .sendError(
0879: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0880: "Cannot transform: "
0881: + ee.getMessage());
0882: return;
0883: }
0884: }
0885: } catch (SOAPException ee) {
0886: response
0887: .sendError(
0888: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
0889: "Unable to write SOAP response: "
0890: + ee.getMessage());
0891: return;
0892: }
0893:
0894: os.flush();
0895: }
0896:
0897: /**
0898: * This class creates a workflow engine reference, and, when available
0899: * receives events and forwards them to the WfXmlAuditHandler.
0900: *
0901: * @author Dirk Schnelle
0902: * @author Michael Lipp
0903: */
0904: private class AuditHandlerThread extends Thread {
0905: private String applicationPolicy;
0906: private String userName;
0907: private String password;
0908: private boolean running = true;
0909:
0910: /**
0911: * Constructs a new object.
0912: */
0913: public AuditHandlerThread(String applicationPolicy,
0914: String userName, String password) {
0915: setDaemon(true);
0916:
0917: this .applicationPolicy = applicationPolicy;
0918: this .userName = userName;
0919: this .password = password;
0920:
0921: }
0922:
0923: /* (non-Javadoc)
0924: * @see java.lang.Thread#run()
0925: */
0926: public void run() {
0927: try {
0928: LoginContext loginContext = new AuditHandlerLoginContext(
0929: applicationPolicy, userName, password);
0930: loginContext.login();
0931: } catch (LoginException e) {
0932: logger.error("login error", e);
0933: return;
0934: }
0935:
0936: WorkflowService wfs = null;
0937: while (running && wfs == null) {
0938: try {
0939: wfs = getWorkflowService();
0940: } catch (FactoryConfigurationError fce) {
0941: try {
0942: sleep(500);
0943: } catch (InterruptedException e) {
0944: return;
0945: }
0946: }
0947: }
0948:
0949: try {
0950: eventSubscriber = wfs.createEventSubscriber();
0951: } catch (IOException e) {
0952: logger.error("Cannot create event subscriber: "
0953: + e.getMessage(), e);
0954: return;
0955: }
0956:
0957: // Now handle events
0958: WfXmlAuditHandler handler = new WfXmlAuditHandler(wfs,
0959: observerRegistry);
0960: while (running) {
0961: try {
0962: WfAuditEvent event = eventSubscriber.receive();
0963: handler.receiveEvent(event);
0964: } catch (Exception e) {
0965: logger.warn("Cannot handle event (ignored): "
0966: + e.getMessage());
0967: }
0968: }
0969: }
0970:
0971: /**
0972: * Preliminarily terminate the running thread.
0973: */
0974: public void terminate() {
0975: running = false;
0976: interrupt();
0977: }
0978:
0979: }
0980:
0981: /**
0982: * Simple login context for authentication.
0983: * @author Dirk Schnelle
0984: */
0985: private static class AuditHandlerLoginContext extends LoginContext {
0986:
0987: private static class CBH implements CallbackHandler {
0988: private String userName = null;
0989: private String password = null;
0990:
0991: public CBH(String userName, String password) {
0992: this .userName = userName;
0993: this .password = password;
0994: }
0995:
0996: public void handle(Callback[] callbacks)
0997: throws UnsupportedCallbackException, IOException {
0998: for (int i = 0; i < callbacks.length; i++) {
0999: if (callbacks[i] instanceof TextOutputCallback) {
1000: // display the message according to the specified type
1001: TextOutputCallback toc = (TextOutputCallback) callbacks[i];
1002: switch (toc.getMessageType()) {
1003: case TextOutputCallback.INFORMATION:
1004: if (logger.isInfoEnabled()) {
1005: logger.info(toc.getMessage());
1006: }
1007: break;
1008: case TextOutputCallback.ERROR:
1009: logger.error("ERROR: " + toc.getMessage());
1010: break;
1011: case TextOutputCallback.WARNING:
1012: logger.warn("WARNING: " + toc.getMessage());
1013: break;
1014: default:
1015: throw new IOException(
1016: "Unsupported message type: "
1017: + toc.getMessageType());
1018: }
1019: } else if (callbacks[i] instanceof NameCallback) {
1020: // prompt the user for a username
1021: NameCallback nc = (NameCallback) callbacks[i];
1022: nc.setName(userName);
1023: } else if (callbacks[i] instanceof PasswordCallback) {
1024: // prompt the user for sensitive information
1025: PasswordCallback pc = (PasswordCallback) callbacks[i];
1026: pc.setPassword(password.toCharArray());
1027: } else if (callbacks[i]
1028: .getClass()
1029: .getName()
1030: .equals(
1031: "weblogic.security.auth.callback.URLCallback")) {
1032: // deliberately ignored.
1033: } else {
1034: throw new UnsupportedCallbackException(
1035: callbacks[i],
1036: "Unrecognized Callback \""
1037: + callbacks[i].getClass()
1038: .getName() + "\"");
1039: }
1040: }
1041: }
1042: }
1043:
1044: public AuditHandlerLoginContext(String applicationPolicy,
1045: String userName, String password) throws LoginException {
1046: super (applicationPolicy, new CBH(userName, password));
1047: }
1048: }
1049: }
|