/* 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 javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.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. * */