/* Wotonomy: OpenStep design patterns for pure Java applications. Copyright (C) 2001 Intersect Software Corporation This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org */ package net.wotonomy.web.xml; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Hashtable; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import net.wotonomy.foundation.NSSelector; import net.wotonomy.foundation.internal.WotonomyException; /** * A servlet that can make any java object into an XML-RPC server. Simply pass * in the object to the constructor and XML-RPC requests to this servlet will * call the appropriate methods and convert the results to an XML-RPC response. *
*
* * Depending on your servlet container, it may be necessary to create a simple * subclass that creates your handler object in its constructor and then calls * setHandler().
*
* * Responses are in the specification's standard response format.
*
* * Faults are returned if any exception is thrown in the method, or if the * specified method is not found on the object. The fault string is the toString * value of the exception, and the fault code is the hasCode value of the * exception.
*
* * Remember that this servlet only responds to POSTs, per the XML-RPC spec. */ public class XMLRPCServlet extends HttpServlet { protected Object handler; protected boolean synchronizing; private Hashtable selectorCache = new Hashtable(); // thread safe private boolean copyStream = false; /** * Default constructor initializes internal state. */ public XMLRPCServlet() { handler = null; synchronizing = false; } /** * Constructor takes any java object and allows its methods to be invoked via * XMLRPC requests to this servlet. Simply calls setHandler(). */ public XMLRPCServlet(Object aHandler) { this(); setHandler(aHandler); } /** * Gets the object whose methods will be invoked to handle incoming requests. */ public Object getHandler() { return handler; } /** * Sets the object whose methods will be invoked to handle incoming requests. */ public void setHandler(Object aHandler) { handler = aHandler; } /** * Gets whether the servlet should synchonize on the object before invoking * methods on it. Defaults to false. */ public boolean isSynchronizing() { return synchronizing; } /** * Sets whether the servlet should synchonize on the object before invoking * methods on it. Defaults to false. */ public void setSynchronizing(boolean willSynchronize) { synchronizing = willSynchronize; } /** * Overridden to service the request. */ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (getHandler() != null) { InputStream input = req.getInputStream(); byte[] copyOfRequest = null; if (copyStream) { ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); int b; while ((b = input.read()) != -1) { byteArray.write(b); } copyOfRequest = byteArray.toByteArray(); input = new ByteArrayInputStream(copyOfRequest); } try { new XMLRPCDecoder().decode(input, new Receiver(this, resp)); } catch (WotonomyException exc) { if (copyOfRequest != null) { System.out.println(new String(copyOfRequest)); exc.printStackTrace(); } // catches io exceptions thrown in handleRequest. Throwable t = exc.getWrappedThrowable(); if (t instanceof IOException) { throw (IOException) t; } throw exc; } } } /** * Called by doPost after parsing an incoming request, and is responsible for * invoking the specified method with the specified parameters on the handler * object. (This implementation calls getOutputStream() on the response.) * Override to customize the handling of the request. */ protected void handleRequest(String aMethodName, Object[] aParameterArray, HttpServletResponse aResponse) { OutputStream output = null; try { output = aResponse.getOutputStream(); aResponse.setStatus(HttpServletResponse.SC_OK); // always 200 aResponse.setContentType("text/xml"); } catch (IOException exc) { // caught in doPost throw new WotonomyException(exc); } // get the array of types XMLRPCEncoder encoder = new XMLRPCEncoder(); Class[] types = new Class[aParameterArray.length]; for (int i = 0; i < aParameterArray.length; i++) { types[i] = aParameterArray[i].getClass(); } // TODO: selectors should be cached if possible Object handler = getHandler(); if (isSynchronizing()) { synchronized (handler) { execute(encoder, handler, output, new NSSelector(aMethodName, types), aParameterArray); } } else { execute(encoder, handler, output, new NSSelector(aMethodName, types), aParameterArray); } } private void execute(XMLRPCEncoder anEncoder, Object aHandler, OutputStream output, NSSelector aSelector, Object[] aParameterArray) { try { Object result = aSelector.invoke(aHandler, aParameterArray); anEncoder.encodeResponse(result, output); } catch (Exception exc) { anEncoder.encodeFault(exc.hashCode(), exc.toString(), output); } } private class Receiver implements XMLRPCReceiver { XMLRPCServlet controller; HttpServletResponse response; public Receiver(XMLRPCServlet aController, HttpServletResponse aResponse) { controller = aController; response = aResponse; } public void request(String aMethodName, Object[] aParameterArray) { controller.handleRequest(aMethodName, aParameterArray, response); } public void response(Object aResult) { // does nothing } public void fault(int aFaultCode, String aFaultString) { // does nothing } } } /* * $Log$ Revision 1.1 2006/02/19 01:44:02 cgruber Add xmlrpc files Remove jclark * and replace with dom4j and javax.xml.sax stuff Re-work dependencies and * imports so it all compiles. * * Revision 1.1 2006/02/16 13:22:22 cgruber Check in all sources in * eclipse-friendly maven-enabled packages. * * Revision 1.1 2001/02/07 19:24:28 mpowers Moved XML classes to separate * package. * */