1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
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
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 );
185 aResponse.setContentType( "text/xml" );
186 }
187 catch ( IOException exc )
188 {
189
190 throw new WotonomyException( exc );
191 }
192
193
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
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
259 }
260
261 public void fault(
262 int aFaultCode, String aFaultString)
263 {
264
265 }
266 }
267 }
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283