/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.piazza.commons.soap; import hk.hku.cecid.piazza.commons.io.IOHandler; import hk.hku.cecid.piazza.commons.servlet.RequestListenerException; import hk.hku.cecid.piazza.commons.util.Instance; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.soap.Name; import javax.xml.soap.SOAPBody; import javax.xml.soap.SOAPBodyElement; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPException; import javax.xml.soap.SOAPMessage; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * WebServicesAdaptor is a SOAPHttpAdaptor which handles web services request. * This adaptor can only handles document style web services. It invokes either * one of the following methods in the subclass for handling the request: * <ol> * <li> public Element[] serviceRequested(Element[] bodies) * <li> public SOAPElement[] serviceRequested(SOAPElement[] bodies) * <li> public void serviceRequested(SOAPMessage req, SOAPMessage resp) * <li> public void serviceRequested(WebServicesRequest req, WebServicesResponse * resp) * </ol> * * @author Hugo Y. K. Lam * */ public abstract class WebServicesAdaptor extends SOAPHttpAdaptor { private static final String SERVICE_METHOD = "serviceRequested"; private static final Class[] SM_DOM_ELEM_PARA_TYPES = new Class[]{Element[].class}; private static final Class[] SM_SOAP_ELEM_PARA_TYPES = new Class[]{SOAPElement[].class}; private static final Class[] SM_SOAP_MSG_PARA_TYPES = new Class[]{SOAPMessage.class, SOAPMessage.class }; private static final Class[] SM_WS_MSG_PARA_TYPES = new Class[]{WebServicesRequest.class, WebServicesResponse.class}; /** * Prcoesses the SOAP request. * * @param request the SOAP request. * @param response the SOAP response. * @throws SOAPRequestException if there is any error in processing the SOAP * request. * @see hk.hku.cecid.piazza.commons.soap.SOAPRequestListener#processRequest(hk.hku.cecid.piazza.commons.soap.SOAPRequest, * hk.hku.cecid.piazza.commons.soap.SOAPResponse) */ public void processRequest(SOAPRequest request, SOAPResponse response) throws SOAPRequestException { try { /* * Extract the body elements from the SOAP request. */ SOAPMessage requestMessage = request.getMessage(); SOAPBody requestBody = requestMessage.getSOAPPart().getEnvelope() .getBody(); Iterator requestBodyChild = requestBody.getChildElements(); ArrayList requestBodies = new ArrayList(); while (requestBodyChild.hasNext()) { Object requestBodyElement = requestBodyChild.next(); if (requestBodyElement instanceof SOAPBodyElement) { requestBodies.add(requestBodyElement); } } SOAPBodyElement[] requestBodyElements = (SOAPBodyElement[]) requestBodies .toArray(new SOAPBodyElement[]{}); Instance thisInstance = new Instance(this); /* * Prepare the response message. */ SOAPMessage responseMessage = response.getMessage(); SOAPBody responseBody = responseMessage.getSOAPPart().getEnvelope() .getBody(); /* * Invoke the method serviceRequest(SOAPElement[] bodies). */ if (thisInstance.isMethodExist(SERVICE_METHOD, SM_SOAP_ELEM_PARA_TYPES)) { SOAPElement[] responseBodyElements = (SOAPElement[]) thisInstance .invoke(SERVICE_METHOD, SM_SOAP_ELEM_PARA_TYPES, new Object[]{requestBodyElements}); addChild(responseBody, responseBodyElements); } /* * Invoke the method serviceRequest(Element[] bodies). */ else if (thisInstance.isMethodExist(SERVICE_METHOD, SM_DOM_ELEM_PARA_TYPES)) { Element[] responseBodyElements = (Element[]) thisInstance .invoke(SERVICE_METHOD, SM_DOM_ELEM_PARA_TYPES, new Object[]{requestBodyElements}); addChild(responseBody, responseBodyElements); } /* * Invoke the method serviceRequest(SOAPMessage req, SOAPMessage * resp). */ else if (thisInstance.isMethodExist(SERVICE_METHOD, SM_SOAP_MSG_PARA_TYPES)) { thisInstance.invoke(SERVICE_METHOD, SM_SOAP_MSG_PARA_TYPES, new Object[]{request.getMessage(), response.getMessage()}); } /* * Invoke the method serviceRequest(WebServicesRequest req, * WebServicesResponse resp). */ else if (thisInstance.isMethodExist(SERVICE_METHOD, SM_WS_MSG_PARA_TYPES)) { WebServicesRequest req = new WebServicesRequest(request); WebServicesResponse resp = new WebServicesResponse(response); req.setBodies(requestBodyElements); thisInstance.invoke(SERVICE_METHOD, SM_WS_MSG_PARA_TYPES, new Object[]{req, resp}); addChild(responseBody, resp.getBodies()); } /* * Initiate a SOAP fault if no service method found. */ else { throw new SOAPRequestException("Unable to find service method"); } } catch (Exception e) { throw new SOAPRequestException("Error in processing SOAP request", e); } } /** * Adds child elements to the given SOAP body according to the their types. * * @param body the SOAP body. * @param elements the child elements to be added. * @throws SOAPException if unable to add any of the elements. */ private void addChild(SOAPBody body, Element[] elements) throws SOAPException { if (elements!=null) { for (int i = 0; i < elements.length; i++) { if (elements[i] instanceof SOAPElement) { body.addChildElement((SOAPElement)elements[i]); } else { body.appendChild(body.getOwnerDocument().importNode(elements[i], true)); } } } } /** * Responses with a web services descriptor if the request's query is "wsdl" * or the request is a "get" method. * * @param request the servlet request. * @param response the servlet response. * @return true if the request is not a "wsdl" request, false otherwise. * @throws RequestListenerException if there is any error in processing the * request. * @see hk.hku.cecid.piazza.commons.servlet.http.HttpRequestListener#doStartRequest(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse) */ public boolean doStartRequest(HttpServletRequest request, HttpServletResponse response) throws RequestListenerException { if ("get".equalsIgnoreCase(request.getMethod()) || "wsdl".equalsIgnoreCase(request.getQueryString())) { OutputStream outs; try { outs = response.getOutputStream(); } catch (Exception e) { throw new RequestListenerException( "Error in retrieving the servlet output stream", e); } response.setContentType("text/xml;utf-8"); processDescriptorRequest(outs); return false; } else { return true; } } /** * Processes the WSDL request. Override to provide specific descriptor. * * @param outs the output stream to which the descriptor should be written. * @throws RequestListenerException if there is error in processing the WSDL * request. */ protected void processDescriptorRequest(OutputStream outs) throws RequestListenerException { try { String descriptor = this.getClass().getName().replace('.', '/') + ".wsdl"; InputStream ins = this.getClass().getClassLoader() .getResourceAsStream(descriptor); if (ins != null) { IOHandler.pipe(ins, outs); } else { String wsdl = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<wsdl>WSDL not available.</wsdl>"; outs.write(wsdl.getBytes()); } } catch (Exception e) { throw new RequestListenerException( "Error in processing WSDL request", e); } } /** * Gets the text value of the element with the specified node name in the * given elements. * * @param elements the elements containing the target node. * @param nodename the target node name. * @return the text value of the specified node. */ protected String getText(Element[] elements, String nodename) { for (int i=0; elements !=null && i < elements.length; i++) { String localname = elements[i].getLocalName(); if (localname.equals(nodename)) { String text = new String(); NodeList nl = elements[i].getChildNodes(); for (int j = 0; j < nl.getLength(); j++) { Node node = nl.item(j); if (node != null) { text = text + node.getNodeValue(); } } text = text.trim(); if (text.equals("")) { return null; } else { return text; } } } return null; } /** * Creates a text element. * * @param name the element name. * @param value the element value. * @param namespace the namespace. * @return the newly created text element. * @throws SOAPException if unable to create the element. */ protected SOAPElement createText(String name, String value, String namespace) throws SOAPException { return createElement(name, value, namespace, "xsd:string"); } /** * Creates a SOAP element. * * @param name the element name. * @param value the element value. * @param namespace the namespace. * @param xsdType the XSD data type. * @return the newly created element. * @throws SOAPException if unable to create the element. */ protected SOAPElement createElement(String name, String value, String namespace, String xsdType) throws SOAPException { Name elementName = super.soapFactory.createName(name, "", namespace); Name elementType = super.soapFactory.createName("type", "", "http://www.w3.org/2001/XMLSchema-instance"); SOAPElement element = super.soapFactory.createElement(elementName); element.addNamespaceDeclaration("xsd", "http://www.w3.org/2001/XMLSchema"); element.addAttribute(elementType, xsdType); element.addTextNode(value); return element; } }