View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2001 Intersect Software Corporation
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.web.xml;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.util.Hashtable;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import net.wotonomy.foundation.NSSelector;
34  import net.wotonomy.foundation.internal.WotonomyException;
35  
36  /***
37  * A servlet that can make any java object into an XML-RPC server.
38  * Simply pass in the object to the constructor and XML-RPC requests
39  * to this servlet will call the appropriate methods and convert
40  * the results to an XML-RPC response. <br><br>
41  *
42  * Depending on your servlet container, it may be necessary to 
43  * create a simple subclass that creates your handler object
44  * in its constructor and then calls setHandler(). <br><br>
45  *
46  * Responses are in the specification's standard response format.<br><br>
47  *
48  * Faults are returned if any exception is thrown in the method,
49  * or if the specified method is not found on the object.
50  * The fault string is the toString value of the exception,
51  * and the fault code is the hasCode value of the exception. <br><br>
52  *
53  * Remember that this servlet only responds to POSTs, 
54  * per the XML-RPC spec.  
55  */
56  public class XMLRPCServlet extends HttpServlet 
57  {
58      protected Object handler;
59      protected boolean synchronizing;
60      private Hashtable selectorCache = new Hashtable(); // thread safe
61      private boolean copyStream = false;
62      
63      /***
64      * Default constructor initializes internal state.
65      */
66      public XMLRPCServlet()
67      {
68          handler = null;
69          synchronizing = false;
70      }
71      
72      /***
73      * Constructor takes any java object and allows its methods
74      * to be invoked via XMLRPC requests to this servlet.
75      * Simply calls setHandler().
76      */
77      public XMLRPCServlet( Object aHandler )
78      {
79          this();
80          setHandler( aHandler );
81      }
82      
83      /***
84      * Gets the object whose methods will be invoked to
85      * handle incoming requests.
86      */
87      public Object getHandler()
88      {
89          return handler;
90      }
91      
92      /***
93      * Sets the object whose methods will be invoked to
94      * handle incoming requests.
95      */
96      public void setHandler( Object aHandler )
97      {
98          handler = aHandler;
99      }
100     
101     /***
102     * Gets whether the servlet should synchonize on the
103     * object before invoking methods on it.
104     * Defaults to false.
105     */
106     public boolean isSynchronizing()
107     {
108         return synchronizing;
109     }
110     
111     /***
112     * Sets whether the servlet should synchonize on the
113     * object before invoking methods on it.
114     * Defaults to false.
115     */
116     public void setSynchronizing( boolean willSynchronize )
117     {
118         synchronizing = willSynchronize;
119     }
120     
121     /***
122     * Overridden to service the request.
123     */
124     protected void doPost(
125         HttpServletRequest req, HttpServletResponse resp) 
126     throws ServletException, IOException
127     {
128         if ( getHandler() != null )
129         {
130             InputStream input = req.getInputStream();
131             byte[] copyOfRequest = null;
132 
133             if ( copyStream )
134             {
135                 ByteArrayOutputStream byteArray =
136                     new ByteArrayOutputStream();
137                 int b;
138                 while ( ( b = input.read() ) != -1 )
139                 {
140                     byteArray.write( b );
141                 }
142                 copyOfRequest = byteArray.toByteArray();
143                 input = new ByteArrayInputStream( copyOfRequest );
144             }
145             
146             try
147             {
148                 new XMLRPCDecoder().decode( input, 
149                     new Receiver( this, resp ) );
150             }
151             catch ( WotonomyException exc )
152             {
153                 if ( copyOfRequest != null )
154                 {
155                     System.out.println( new String( copyOfRequest ) );
156                     exc.printStackTrace();
157                 }
158                 // catches io exceptions thrown in handleRequest.
159                 Throwable t = exc.getWrappedThrowable();
160                 if ( t instanceof IOException )
161                 {
162                     throw (IOException)t;
163                 }
164                 throw exc;
165             }
166         }
167     }
168     
169     /***
170     * Called by doPost after parsing an incoming request,
171     * and is responsible for invoking the specified method
172     * with the specified parameters on the handler object.
173     * (This implementation calls getOutputStream() on the response.)
174     * Override to customize the handling of the request.
175     */
176     protected void handleRequest( String aMethodName, 
177         Object[] aParameterArray, HttpServletResponse aResponse )
178     {
179         OutputStream output = null;
180         
181         try 
182         {
183             output = aResponse.getOutputStream();
184             aResponse.setStatus( HttpServletResponse.SC_OK ); // always 200
185             aResponse.setContentType( "text/xml" );
186         }
187         catch ( IOException exc )
188         {
189             // caught in doPost
190             throw new WotonomyException( exc );   
191         }
192         
193         // get the array of types
194         XMLRPCEncoder encoder = new XMLRPCEncoder();
195         Class[] types = new Class[ aParameterArray.length ];
196         for ( int i = 0; i < aParameterArray.length; i++ )
197         {
198             types[i] = aParameterArray[i].getClass();
199         }
200         
201         //TODO: selectors should be cached if possible
202         
203         Object handler = getHandler();
204         if ( isSynchronizing() )
205         {
206             synchronized ( handler )
207             {
208                 execute( encoder, handler, output,
209                     new NSSelector( aMethodName, types ), aParameterArray );
210             }
211         }
212         else
213         {
214             execute( encoder, handler, output,
215                 new NSSelector( aMethodName, types ), aParameterArray );
216         }
217     }
218     
219     private void execute( XMLRPCEncoder anEncoder, Object aHandler, 
220         OutputStream output, NSSelector aSelector, Object[] aParameterArray )
221     {
222         try 
223         {
224             Object result = 
225                 aSelector.invoke( aHandler, aParameterArray );
226             anEncoder.encodeResponse( result, output );
227         }
228         catch ( Exception exc )
229         {
230             anEncoder.encodeFault( 
231                 exc.hashCode(), exc.toString(), output );
232         }
233     }
234     
235     private class Receiver implements XMLRPCReceiver
236     {
237         XMLRPCServlet controller;
238         HttpServletResponse response;
239         
240         public Receiver( 
241             XMLRPCServlet aController,
242             HttpServletResponse aResponse )
243         {
244             controller = aController;
245             response = aResponse;
246         }
247         
248         public void request( 
249             String aMethodName, Object[] aParameterArray )
250         {
251             controller.handleRequest( 
252                 aMethodName, aParameterArray, response );
253         }
254         
255         public void response( 
256             Object aResult ) 
257         {
258             // does nothing
259         }
260         
261         public void fault( 
262             int aFaultCode, String aFaultString)
263         {
264             // does nothing
265         }
266     }
267 }
268 
269 /*
270  * $Log$
271  * Revision 1.1  2006/02/19 01:44:02  cgruber
272  * Add xmlrpc files
273  * Remove jclark and replace with dom4j and javax.xml.sax stuff
274  * Re-work dependencies and imports so it all compiles.
275  *
276  * Revision 1.1  2006/02/16 13:22:22  cgruber
277  * Check in all sources in eclipse-friendly maven-enabled packages.
278  *
279  * Revision 1.1  2001/02/07 19:24:28  mpowers
280  * Moved XML classes to separate package.
281  *
282  */
283