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

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

/*
* This file is part of the WfXML servlet.
* 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: Servlet.java 2431 2007-07-15 22:01:11Z mlipp $
*
* $Log$
* Revision 1.24  2007/04/03 14:47:54  schnelle
* Some code cleanup.
*
* Revision 1.23  2007/03/29 11:46:54  schnelle
* Reactivated ASAPException to propagate ASAP error messages in cases of an invalid key, a missing resource or an invalid factory.
*
* Revision 1.22  2007/03/27 21:59:42  mlipp
* Fixed lots of checkstyle warnings.
*
* Revision 1.21  2007/03/01 12:32:57  schnelle
* Enhanced Instance.SetProperties to process ContextData.
*
* Revision 1.20  2007/02/21 21:32:29  mlipp
* Using pooled JMS connections when in EJB now.
*
* Revision 1.19  2007/02/17 21:19:48  mlipp
* Workflow service caching redone.
*
* Revision 1.18  2007/02/06 08:35:34  schnelle
* Started automatic generation of wsdl description.
*
* Revision 1.17  2007/02/01 10:08:36  drmlipp
* Removed no longer used observer resource.
*
* Revision 1.16  2007/01/31 22:55:36  mlipp
* Some more refactoring and fixes of problems introduced by refactoring.
*
* Revision 1.15  2007/01/31 14:53:06  schnelle
* Small corrections wvaluating the resource reference.
*
* Revision 1.14  2007/01/31 12:24:06  drmlipp
* Design revisited.
*
* Revision 1.13  2007/01/30 11:56:14  drmlipp
* Merged Wf-XML branch.
*
* Revision 1.12.6.24  2007/01/29 15:04:19  schnelle
* Renaming of Observer to ObserverRegistry and URIDecoder to ResourceReference.
*
* Revision 1.12.6.23  2007/01/29 13:40:31  schnelle
* Storing of the sender base in the servlet context.
*
* Revision 1.12.6.22  2007/01/29 10:48:28  schnelle
* Using a key-value encoding of the parameters instead of the directory approach.
*
* Revision 1.12.6.21  2007/01/26 15:50:28  schnelle
* Added encoding for process id and package id.
*
* Revision 1.12.6.20  2007/01/24 14:22:38  schnelle
* Observer handler starts on servlet startup.
*
* Revision 1.12.6.19  2007/01/24 10:56:50  schnelle
* Prepared return of a result for aobservers.
*
* Revision 1.12.6.18  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.12.6.17  2007/01/19 07:59:35  schnelle
* Corrected return value for factory list instances.
*
* Revision 1.12.6.16  2007/01/16 11:05:42  schnelle
* Refactoring: Moved subscription handling methods to own class.
*
* Revision 1.12.6.15  2007/01/11 10:23:52  schnelle
* Creation of StateChanged notifications.
*
* Revision 1.12.6.14  2007/01/10 13:41:27  schnelle
* Implemented subscribe.
*
* Revision 1.12.6.13  2007/01/09 12:06:14  schnelle
* Implemented methods to receive xsd and wsdl files.
*
* Revision 1.12.6.12  2006/12/20 13:32:24  schnelle
* Basic implementato of GetProperties for Instance and Activity.
*
* Revision 1.12.6.11  2006/12/18 14:41:02  schnelle
* Preparatation for individual schema definition for each getproperties request.
*
* Revision 1.12.6.10  2006/12/13 11:23:48  schnelle
* Implemented instance ListActivities.
*
* Revision 1.12.6.9  2006/12/12 13:24:38  schnelle
* Introduction of ASAPException to provide a detailed mesage.
*
* Revision 1.12.6.8  2006/12/12 09:34:35  schnelle
* Implemented ChangeState for Instance.
*
* Revision 1.12.6.7  2006/12/11 11:05:34  schnelle
* Added template methods for all requests.
*
* Revision 1.12.6.6  2006/12/01 12:49:54  schnelle
* Basic import of context data for process creation.
*
* Revision 1.12.6.5  2006/11/29 14:12:37  schnelle
* Take respect to namespaces of asap requests and responses.
*
* Revision 1.12.6.4  2006/11/28 15:31:51  schnelle
* Proper selection of the response generator.
*
* Revision 1.12.6.3  2006/11/28 12:20:09  schnelle
* Creation of a separate class to handle the issues for a specific resource.
*
* Revision 1.12.6.2  2006/11/27 15:41:55  schnelle
* Introducing some constants for request and response identification.
*
* Revision 1.12.6.1  2006/11/24 12:19:13  schnelle
* Separtion of response generation into ResponseGenerator class.
*
* Revision 1.12  2006/09/29 12:32:11  drmlipp
* Consistently using WfMOpen as projct name now.
*
* Revision 1.11  2005/11/17 21:30:25  mlipp
* Finished re-organization of jetspeed module creation.
*
* Revision 1.10  2005/06/18 19:43:12  mlipp
* Improved.
*
* Revision 1.9  2005/06/01 20:46:16  mlipp
* Started getProperties.
*
* Revision 1.8  2005/04/24 18:55:34  mlipp
* Fixed bug with scalar result.
*
* Revision 1.7  2005/04/11 20:22:18  mlipp
* Implemented enabled process definition list.
*
* Revision 1.6  2005/04/10 20:59:30  mlipp
* Improved exception handling.
*
* Revision 1.5  2005/04/10 19:53:54  mlipp
* Valid response and JaWE workaround.
*
* Revision 1.4  2005/04/06 21:08:05  mlipp
* Getting on...
*
* Revision 1.3  2005/04/06 20:03:26  mlipp
* Generating web.xml now.
*
* Revision 1.2  2005/01/24 20:25:56  mlipp
* Reverted saaj back to 1.1 to fit Axis version.
*
* Revision 1.1  2005/01/23 15:23:11  mlipp
* Getting started with WfXML.
*
*/
package de.danet.an.workflow.clients.wfxml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.rmi.RemoteException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.naming.NamingException;
import javax.naming.directory.InvalidSearchFilterException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.wsdl.Binding;
import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.extensions.schema.SchemaImport;
import javax.wsdl.extensions.soap.SOAPAddress;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.Name;
import javax.xml.soap.Node;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import com.ibm.wsdl.extensions.soap.SOAPAddressImpl;
import com.ibm.wsdl.factory.WSDLFactoryImpl;

import de.danet.an.util.XMLUtil;
import de.danet.an.workflow.api.EventSubscriber;
import de.danet.an.workflow.api.FactoryConfigurationError;
import de.danet.an.workflow.api.WorkflowService;
import de.danet.an.workflow.api.WorkflowServiceFactory;
import de.danet.an.workflow.omgcore.WfAuditEvent;

/**
* This class provides an interface between the workflow engine (i.e. its
* processes and any HTTP based client (e.g. Web browser application).
*
* <p>
* The servlet works as a mediator for services of the different roles,
* excluding the observer, as they are defined by the Wf-XML 2.0 standard.
* </p>
*
* @author Michael Lipp
* @author Dirk Schnelle
* @version $Revision: 2431 $
* @web.servlet name="WfXML Servlet" display-name="WfXML Servlet"
*              description="WfXML interface servlet" load-on-startup="1"
* @web.servlet-mapping url-pattern="/*"
* @web.servlet-init-param name="SenderBase" value="@@@_WfXML_service_url_@@@"
* @web.servlet-init-param name="ApplicationPolicy"
*                         value="@@@_WfXML_servlet_application_policy_@@@"
* @web.servlet-init-param name="UserName"
*                         value="@@@_WfXML_servlet_username_@@@"
* @web.servlet-init-param name="Password"
*                         value="@@@_WfXML_servlet_password_@@@"
* @web.resource-ref name="jdbc/WfEngine" type="javax.sql.DataSource"
*                   auth="Container"
* @jboss.resource-ref res-ref-name="jdbc/WfEngine" jndi-name="java:/WfMOpenDS"
*/
public class Servlet extends HttpServlet {
    /** Logger instance. */
    private static final org.apache.commons.logging.Log logger
        = org.apache.commons.logging.LogFactory.getLog(Servlet.class);

    /** Attribute sender base in the servlet context. */
    private static final String SENDER_BASE = "SenderBase";

    /** Attribute observer registry in the servlet context. */
    static final String OBSERVER_REGISTRY = "ObserverRegistry";
   
    /** The servlet context. */
    private ServletContext ctx = null;

    /** Audit handler thread. */
    private AuditHandlerThread auditHandlerThread = null;
   
    /** Cache of the last workflow service lookup. */
    private WorkflowService wfsCache = null;

    /** Base URL that is used as a prefix in the sender key. */
    private String senderBase;

    /** The observer registry. */
    private ObserverRegistry observerRegistry = null;
   
    /** The event subscriber */
    private EventSubscriber eventSubscriber = null;
   
    /**
     * Request workflow service.
     */
    private synchronized WorkflowService getWorkflowService() {
        synchronized (this) {
            if (wfsCache == null) {
                WorkflowServiceFactory factory
                    = WorkflowServiceFactory.newInstance();
                wfsCache = factory.newWorkflowService();
            }
            return wfsCache;
        }
    }

    /**
     * Default initialization method.
     *
     * @param servletConfig
     *            a <code>ServletConfig</code> value
     * @exception ServletException
     *                if an error occurs
     */
    public void init(ServletConfig servletConfig) throws ServletException {
        ctx = servletConfig.getServletContext();

        try {
            observerRegistry = new ObserverRegistry();
            ctx.setAttribute(OBSERVER_REGISTRY, observerRegistry);
        } catch (NamingException e) {
            throw new ServletException (e);
        }
       
        // Read configuration values and overwrite default, if set
        if (servletConfig != null) {
            senderBase = servletConfig.getInitParameter(SENDER_BASE);
            String applicationPolicy
                = servletConfig.getInitParameter("ApplicationPolicy");
            String userName
                = servletConfig.getInitParameter("UserName");
            String password
                = servletConfig.getInitParameter("Password");
            // The workflow engine may not yet be available, when the servlet
            // is started. Repeat trying to subscribe until we are successful.
            auditHandlerThread
                = new AuditHandlerThread(applicationPolicy, userName, password);
            auditHandlerThread.start();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.GenericServlet#destroy()
     */
    public void destroy() {
        if (auditHandlerThread != null) {
            auditHandlerThread.terminate();
            try {
                auditHandlerThread.join();
            } catch (InterruptedException e) {
                // deliberately ignored
            }
            auditHandlerThread = null;
        }
        if (eventSubscriber != null) {
            wfsCache.release(eventSubscriber);
            eventSubscriber = null;
        }
        if (wfsCache != null) {
            wfsCache.release(wfsCache);
            wfsCache = null;
        }
        super.destroy();
    }

    /**
     * Retrieves the sender base.
     *
     * <p>
     * The sender base is derived from the request. The this value may be
     * overridden via the <code>SenderBase</code> parameter in the
     * <code>web.xml</code>.
     * </p>
     * @param request the servlet request.
     * @return the sender base.
     */
    String getSenderBase(HttpServletRequest request) {
        if (senderBase != null && senderBase.length() > 0) {
            return senderBase;
        }
        return getRequestBasePath(request);
    }
   
    /**
     * Create a new process and forward initial response (e.g. HTML start page).
     * See user manual (chapter "tools") for a detailed description.
     *
     * @param request
     *            a <code>HttpServletRequest</code> value
     * @param response
     *            a <code>HttpServletResponse</code> value
     * @exception ServletException
     *                if an error occurs
     * @exception IOException
     *                if an error occurs
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String requestUrl = request.getRequestURL().toString();
        String query = request.getQueryString();
        if ((query != null) && query.equalsIgnoreCase("wsdl")) {
            getWsdl(request, response);
        } else if (requestUrl.endsWith(".xsd")
                || requestUrl.endsWith(".wsdl")) {
            getFile(request, response);
        } else {
            doPost(request, response);
        }
    }

    /**
     * Returns the contents of the requested file encoded as
     * <code>text/plain</code>.
     *
     * @param request
     * @param response
     * @throws IOException
     *             Error accessing the file.
     */
    private void getWsdl(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        WSDLFactory factory = new WSDLFactoryImpl();
        WSDLReader reader = factory.newWSDLReader();
        WSDLWriter writer = factory.newWSDLWriter();
       
        String wsdllocation = ctx.getRealPath("/wfxml.wsdl");
        File file = new File(wsdllocation);
       
        try {
            Definition definition = reader.readWSDL(file.toURI().getPath());
           
            String requestBasePath = getRequestBasePath(request);
            if (requestBasePath.endsWith("/")) {
                requestBasePath
                    = requestBasePath.substring(0, requestBasePath.length() -1);
            }
           
            definition.setDocumentBaseURI(requestBasePath);
            adjustReferencedElements(definition, requestBasePath);
           
            addWsdlService(request, definition, "WfXmlServiceRegistry",
                    "WfMOpenServiceRegistry", "WfMOpenServiceRegistryService");

            addWsdlService(request, definition, "WfXmlFactory",
                    "WfMOpenFactory", "WfMOpenFactoryService");

            addWsdlService(request, definition, "WfXmlInstance",
                    "WfMOpenInstance", "WfMOpenInstanceService");

            addWsdlService(request, definition, "WfXmlActivity",
                    "WfMOpenActivity", "WfMOpenActivityService");
           
            writer.writeWSDL(definition, response.getOutputStream());
        } catch (WSDLException e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Adjust the import definitions to the path under which we were called.
     * This is necessary, since the referenced resources will be retrieved
     * from a package above the servlet directory, if the servlet
     * is called with <code>http://.../wfxml?wsdl</code>.
     * @param definition the WSDL definition.
     * @param requestBasePath the base path.
     */
    private void adjustReferencedElements(Definition definition,
            String requestBasePath) {
        List imports = definition
            .getImports("http://www.oasis-open.org/asap/0.9/asap.wsdl");
        Import asapImport = (Import) imports.get(0);
        asapImport.setLocationURI(requestBasePath + "/asap.wsdl");

        // TODO: The types are not properly written to the definition.
        Types types = definition.getTypes();
        List typeList = types.getExtensibilityElements();
        Iterator iterator = typeList.iterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            if (o instanceof Schema) {
                Schema schema = (Schema)o;
                Map map = schema.getImports();
               
                List schemaImports = (List)
                    map.get("http://www.wfmc.org/wfxml/2.0/wfxml20.xsd");
                SchemaImport schemaImport = (SchemaImport) schemaImports.get(0);
                Schema ref = schemaImport.getReferencedSchema();
               
                schemaImport
                    .setSchemaLocationURI(requestBasePath + "/wfxml20.xsd");
            }
        }
    }

    /**
     * Adds a service description to the WSDL definition.
     * @param request
     * @param definition
     */
    private void addWsdlService(HttpServletRequest request,
            Definition definition, String bindingName, String portName,
            String serviceName) {
        Service service = definition.createService();
        Port port = definition.createPort();
        QName name = new QName
            ("http://wfmopen.sourceforge.net/wfxml20.wsdl", bindingName, "");
        Binding binding = definition.getBinding(name);
        port.setName(portName);
        port.setBinding(binding);
       
        service.setQName
            (new QName ("http://wfmopen.sourceforge.net/wfxml20.wsdl",
                        serviceName, "tns"));
        service.addPort(port);
        SOAPAddress address = new SOAPAddressImpl();
        address.setLocationURI(getRequestBasePath(request));
       
        port.addExtensibilityElement(address);
       
        definition.addService(service);
    }
   
    /**
     * Returns the contents of the requested file encoded as
     * <code>text/plain</code>.
     *
     * @param request
     * @param response
     * @throws IOException
     *             Error accessing the file.
     */
    private void getFile(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        ServletOutputStream out = response.getOutputStream();
        // Get the file to view
        String file = request.getPathTranslated();

        // No file, nothing to view
        if (file == null) {
            out.println("No file to view");
            return;
        }

        // Get and set the type of the file
        response.setContentType("text/plain");

        // Return the file
        try {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                byte[] buf = new byte[4 * 1024]; // 4K buffer
                int bytesRead;
                while ((bytesRead = fis.read(buf)) != -1) {
                    out.write(buf, 0, bytesRead);
                }
            } finally {
                if (fis != null) {
                    fis.close();
                }
            }
        } catch (FileNotFoundException e) {
            out.println("File not found");
        }
    }

    /**
     * Receive HTTP requests for a channel based access to a workflow process.
     * See user manual (chapter "tools") for a detailed description.
     *
     * @param request
     *            a <code>HttpServletRequest</code> value
     * @param response
     *            a <code>HttpServletResponse</code> value
     * @exception ServletException
     *                if an error occurs
     * @exception IOException
     *                if an error occurs
     */
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession session = request.getSession(true);

        MessageFactory messageFactory = null;
        try {
            messageFactory = MessageFactory.newInstance();
        } catch (SOAPException e) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "Unable to access SOAP subsystem: " + e.getMessage());
            return;
        }

        SOAPMessage respMsg = null;
        WorkflowService wfs = null;
        // workaround JaWE bug
        boolean agentIsJaWE = false;
        Throwable fatalProblem = null;

        try {
            // get mime request headers for building request structure
            MimeHeaders mimeHeaders = new MimeHeaders();
            for (Enumeration en = request.getHeaderNames(); en
                    .hasMoreElements();) {
                String headerName = (String) en.nextElement();
                String headerVal = request.getHeader(headerName);
                if (headerName.equalsIgnoreCase("user-agent")
                        && headerVal.startsWith("JaWE")) {
                    agentIsJaWE = true;
                }
                StringTokenizer tk = new StringTokenizer(headerVal, ",");
                while (tk.hasMoreTokens()) {
                    mimeHeaders.addHeader(headerName, tk.nextToken().trim());
                }
            }
           
            // build request message from headers and stream
            SOAPMessage reqMsg = messageFactory.createMessage(mimeHeaders,
                    request.getInputStream());

            while (true) {
                try {
                    respMsg = messageFactory.createMessage();
                    wfs = getWorkflowService();
                    if (wfs == null) {
                        SOAPFault fault = respMsg.getSOAPPart()
                            .getEnvelope().getBody().addFault();
                        fault.setFaultString
                            ("Unable to access workflow service");
                        return;
                    }

                    AbstractResponseGenerator respgen = getResponseGenerator
                        (getSenderBase(request), reqMsg, wfs);
                    respgen.fillResponseHeader(reqMsg, respMsg);
                    respgen.evaluate(reqMsg, respMsg);

                    break;
                } catch (RemoteException e) {
                    logger.debug("Problem while trying to create response "
                            + "(retrying): " + e.getMessage(), e);
                }
            }
        } catch (SOAPException e) {
            fatalProblem = e;
        } catch (FactoryConfigurationError e) {
            fatalProblem = e;
        } catch (ASAPException e) {
            fatalProblem = e;
        } catch (InvalidSearchFilterException e) {
            fatalProblem = e;
        } finally {
            if (fatalProblem != null) {
                if (logger.isDebugEnabled()) {
                    logger.debug("error executing the client request",
                            fatalProblem);
                }

                try {
                    respMsg = messageFactory.createMessage();
                    if (fatalProblem instanceof ASAPException) {
                        ASAPException cause = (ASAPException) fatalProblem;
                        FaultUtils.setFault
                            (respMsg, cause.getErrorCode(),
                             cause.getMessage());
                    } else {
                        FaultUtils.setFault(respMsg, fatalProblem);
                    }
                } catch (SOAPException ee) {
                    response.sendError
                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Unable to use SOAP subsystem: " + ee.getMessage());
                    return;
                }
            }
        }

        sendResponse(response, respMsg, agentIsJaWE);
    }

    /**
     * Retrieves an {@link AbstractResponseGenerator} instance that is capable
     * to handle the current request.
     *
     * <p>
     * The response generator is determined through evaluation of the
     * <code>&lt;as:RecieverKey&gt;</code> in the header of the request
     * message
     * </p>
     *
     * @param senderBase the base for constructing the sender key
     * @param reqMsg
     *            the SOAP request.
     * @param wfs
     *            reference to the workflow engine.
     *
     * @return Response generator to handle the current request.
     * @throws SOAPException
     *             error evaluating the SOAP header.
     * @throws FileNotFoundException
     *             error obtaining the sender base.
     */
    private AbstractResponseGenerator getResponseGenerator
            (String senderBase, SOAPMessage reqMsg, WorkflowService wfs)
            throws SOAPException, ASAPException, InvalidSearchFilterException {
        // Determine the base request URL as a basis for referenced
        // resources.
        String receiverKey = null;
        receiverKey = getReceiverKey(reqMsg);

        ResourceReference resRef
            = new ResourceReference(senderBase, receiverKey);
       
        ObserverRegistry observerRegistry
            = (ObserverRegistry)ctx.getAttribute(OBSERVER_REGISTRY);
        String resource = resRef.getResource();
        if (resource.equals
            (AbstractResponseGenerator.RESOURCE_SERVICE_REGISTRY)) {
            return new ServiceRegistryResponseGenerator
                (observerRegistry, wfs, resRef);
        }
        if (resource.equals(AbstractResponseGenerator.RESOURCE_FACTORY)) {
            return new FactoryResponseGenerator(observerRegistry, wfs, resRef);
        }
        if (resource.equals(AbstractResponseGenerator.RESOURCE_INSTANCE)) {
            return new InstanceResponseGenerator(observerRegistry, wfs, resRef);
        }
        if (resource.equals(AbstractResponseGenerator.RESOURCE_ACTIVITY)) {
            return new ActivityResponseGenerator(observerRegistry, wfs, resRef);
        }

        // This should not happen, since we trap into an exception, trying
        // to obtain the resource string.
        throw new ASAPException(ASAPException.ASAP_INVALID_FACTORY,
            receiverKey + " cannot be mapped to a known resource.");
    }

    /**
     * Retrieves the base path that was used to call this servlet. Since this
     * servlet may be called with the same URL as it is specified in the
     * <code>SenderKey</code> of the ASAP header, we  have to determine the
     * base path without any extra path info to create the responses.
     * @param request the request.
     * @return base path.
     */
    private String getRequestBasePath(HttpServletRequest request) {
        String requestUrl = request.getRequestURL().toString();
        String servletPath = request.getPathInfo();
       
        // Some implementations may return "/" if there is no extra path info
        // available, although they should return null in that case.
        if ((servletPath == null) || (servletPath.length() <= 1)) {
            return requestUrl;
        }
           
       
        return requestUrl.substring(0, requestUrl.length()
                - servletPath.length());
    }
   
    /**
     * Reads the <code>ReceiverKey</code> from the request header of the given
     * message.
     *
     * @param message
     *            the message to inspect.
     * @return value of the <code>ReceiverKey</code>.
     * @throws SOAPException
     *             error evaluating the message.
     */
    private String getReceiverKey(SOAPMessage message)
        throws SOAPException, ASAPException {
        SOAPHeaderElement headerElement = null;

        SOAPHeader header = message.getSOAPHeader();
        for (Iterator i = header.getChildElements(); i.hasNext();) {
            Node node = (Node) i.next();
            if (node instanceof SOAPHeaderElement) {
                headerElement = (SOAPHeaderElement) node;

                Name headerElementName = headerElement.getElementName();
                String localName = headerElementName.getLocalName();
                if (localName.equals(Consts.REQUEST_HEADER)) {
                    String headerUri = headerElementName.getURI();
                    if (headerUri.equals(Consts.ASAP_NS)
                            || headerUri.equals(Consts.WFXML_NS)) {
                        break;
                    }
                }
            }
        }

        if (headerElement == null) {
            throw new ASAPException(ASAPException.ASAP_ELEMENT_MISSING,
                "ASAP request header not found.");
        }

        for (Iterator i = headerElement.getChildElements(); i.hasNext();) {
            Node node = (Node) i.next();
            if (node instanceof SOAPElement) {
                SOAPElement element = (SOAPElement) node;
                String name = element.getElementName().getLocalName();
                if (name.equals(Consts.RECEIVER_KEY)) {
                    return XMLUtil.getFirstLevelTextContent(element);
                }
            }
        }

        throw new ASAPException(ASAPException.ASAP_ELEMENT_MISSING,
            Consts.SENDER_KEY + " must be specified in the header");
    }

    /**
     * Sends the response.
     *
     * @param response
     *            the servlet's response.
     * @param respMsg
     *            the SOAP response message.
     * @param agentIsJaWE
     *            <code>true</code> if this servlet was called by JaWE.
     * @throws IOException
     *             Error accessing the output stream.
     */
    private void sendResponse(HttpServletResponse response,
            SOAPMessage respMsg, boolean agentIsJaWE) throws IOException {
        response.setHeader("Content-Type", "text/xml");
        OutputStream os = response.getOutputStream();

        try {
            if (!agentIsJaWE) {
                respMsg.writeTo(os);
            } else {
                // workaround JaWE bug. JaWE cannot handle pretty
                // printed XML
                try {
                    TransformerFactory tf = TransformerFactory.newInstance();
                    Transformer t = tf.newTransformer();
                    t.transform(new DOMSource(respMsg.getSOAPPart()
                            .getEnvelope()), new StreamResult(os));
                } catch (TransformerConfigurationException ee) {
                    response.sendError
                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Cannot get transformer: " + ee.getMessage());
                    return;
                } catch (TransformerException ee) {
                    response.sendError
                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Cannot transform: " + ee.getMessage());
                    return;
                }
            }
        } catch (SOAPException ee) {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    "Unable to write SOAP response: " + ee.getMessage());
            return;
        }

        os.flush();
    }

    /**
     * This class creates a workflow engine reference, and, when available
     * receives events and forwards them to the WfXmlAuditHandler.
     *
     * @author Dirk Schnelle
     * @author Michael Lipp
     */
    private class AuditHandlerThread extends Thread {
        private String applicationPolicy;
        private String userName;
        private String password;
        private boolean running = true;
       
        /**
         * Constructs a new object.
         */
        public AuditHandlerThread(String applicationPolicy, String userName,
                String password) {
            setDaemon(true);

            this.applicationPolicy = applicationPolicy;
            this.userName = userName;
            this.password = password;
           
        }

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        public void run() {
            try {
                LoginContext loginContext
                    = new AuditHandlerLoginContext
                        (applicationPolicy, userName, password);
                loginContext.login ();
            } catch (LoginException e) {
                logger.error("login error", e);
                return;
            }

            WorkflowService wfs = null;
            while (running && wfs == null) {
                try {
                    wfs = getWorkflowService();
                } catch (FactoryConfigurationError fce) {
                    try {
                        sleep(500);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }
           
            try {
                eventSubscriber = wfs.createEventSubscriber();
            } catch (IOException e) {
                logger.error
                    ("Cannot create event subscriber: " + e.getMessage(), e);
                return;
            }
           
            // Now handle events
            WfXmlAuditHandler handler
                = new WfXmlAuditHandler (wfs, observerRegistry);
            while (running) {
                try {
                    WfAuditEvent event = eventSubscriber.receive();
                    handler.receiveEvent(event);
                } catch (Exception e) {
                    logger.warn
                        ("Cannot handle event (ignored): " + e.getMessage());
                }
            }
        }

        /**
         * Preliminarily terminate the running thread.
         */
        public void terminate () {
            running = false;
            interrupt ();
        }

    }
   
    /**
     * Simple login context for authentication.
     * @author Dirk Schnelle
     */
    private static class AuditHandlerLoginContext extends LoginContext {

        private static class CBH implements CallbackHandler {
            private String userName = null;
            private String password = null;

            public CBH (String userName, String password) {
                this.userName = userName;
                this.password = password;
            }

            public void handle (Callback[] callbacks)
            throws UnsupportedCallbackException, IOException {
                for (int i = 0; i < callbacks.length; i++) {
                    if (callbacks[i] instanceof TextOutputCallback) {
                        // display the message according to the specified type
                        TextOutputCallback toc
                        = (TextOutputCallback)callbacks[i];
                        switch (toc.getMessageType()) {
                        case TextOutputCallback.INFORMATION:
                            if (logger.isInfoEnabled()) {
                                logger.info(toc.getMessage());
                            }
                            break;
                        case TextOutputCallback.ERROR:
                            logger.error("ERROR: " + toc.getMessage());
                            break;
                        case TextOutputCallback.WARNING:
                            logger.warn("WARNING: " + toc.getMessage());
                            break;
                        default:
                            throw new IOException
                            ("Unsupported message type: "
                                    + toc.getMessageType());
                        }
                    } else if (callbacks[i] instanceof NameCallback) {
                        // prompt the user for a username
                        NameCallback nc = (NameCallback)callbacks[i];
                        nc.setName(userName);
                    } else if (callbacks[i] instanceof PasswordCallback) {
                        // prompt the user for sensitive information
                        PasswordCallback pc = (PasswordCallback)callbacks[i];
                        pc.setPassword(password.toCharArray());
                    } else if (callbacks[i].getClass().getName().equals
                            ("weblogic.security.auth.callback.URLCallback")) {
                        // deliberately ignored.
                    } else {
                        throw new UnsupportedCallbackException
                        (callbacks[i], "Unrecognized Callback \""
                                + callbacks[i].getClass().getName() + "\"");
                    }
                }
            }
        }

        public AuditHandlerLoginContext
        (String applicationPolicy, String userName, String password)
        throws LoginException {
            super (applicationPolicy, new CBH(userName, password));
        }
    }   
}
TOP

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

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.