KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > reading > AxisRPCReader


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.cocoon.reading;
18
19 import java.io.ByteArrayOutputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import javax.servlet.ServletContext JavaDoc;
25 import javax.servlet.http.HttpServletRequest JavaDoc;
26 import javax.servlet.http.HttpServletResponse JavaDoc;
27 import javax.servlet.http.HttpUtils JavaDoc;
28 import javax.xml.soap.SOAPException JavaDoc;
29
30 import org.apache.avalon.framework.activity.Disposable;
31 import org.apache.avalon.framework.configuration.Configurable;
32 import org.apache.avalon.framework.configuration.Configuration;
33 import org.apache.avalon.framework.configuration.ConfigurationException;
34 import org.apache.avalon.framework.parameters.Parameters;
35 import org.apache.avalon.framework.service.ServiceException;
36 import org.apache.avalon.framework.service.ServiceManager;
37
38 import org.apache.axis.AxisFault;
39 import org.apache.axis.Constants;
40 import org.apache.axis.Message;
41 import org.apache.axis.MessageContext;
42 import org.apache.axis.soap.SOAPConstants;
43 import org.apache.axis.transport.http.AxisHttpSession;
44 import org.apache.axis.transport.http.HTTPConstants;
45
46 import org.apache.cocoon.ProcessingException;
47 import org.apache.cocoon.components.axis.SoapServer;
48 import org.apache.cocoon.environment.ObjectModelHelper;
49 import org.apache.cocoon.environment.SourceResolver;
50 import org.apache.cocoon.environment.http.HttpEnvironment;
51
52 import org.w3c.dom.Element JavaDoc;
53 import org.xml.sax.SAXException JavaDoc;
54
55 /**
56  * SOAP Reader
57  *
58  * <p>
59  * This reader accepts a SOAP Request, and generates the resultant
60  * response as output. Essentially, this reader allows you to serve SOAP
61  * requests from your Cocoon application.
62  * </p>
63  *
64  * <p>
65  * Code originates from the Apache
66  * <a HREF="http://xml.apache.org/axis">AXIS</a> project,
67  * <code>org.apache.axis.http.transport.AxisServlet</code>.
68  * </p>
69  *
70  * Ported to Cocoon by:
71  *
72  * @author <a HREF="mailto:crafterm@apache.org">Marcus Crafter</a>
73  *
74  * Original <code>AxisServlet</code> authors:
75  *
76  * @author <a HREF="mailto:">Steve Loughran</a>
77  * @author <a HREF="mailto:dug@us.ibm.com">Doug Davis</a>
78  *
79  * @version CVS $Id: AxisRPCReader.java 149132 2005-01-30 18:24:33Z cziegeler $
80  */

81 public class AxisRPCReader extends ServiceableReader
82                            implements Configurable, Disposable {
83
84     // soap server reference
85
private SoapServer m_server;
86
87     /** Are we in development stage ? */
88     private boolean m_isDevelompent = false;
89
90     /* (non-Javadoc)
91      * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration)
92      */

93     public void configure(Configuration config) throws ConfigurationException {
94         m_isDevelompent = config.getChild("development-stage").getValueAsBoolean(m_isDevelompent );
95     }
96     
97     public void service(final ServiceManager manager) throws ServiceException {
98         super.service(manager);
99         // set soap server reference
100
m_server = (SoapServer) manager.lookup(SoapServer.ROLE);
101     }
102
103     /**
104      * Axis RPC Router <code>setup</code> method.
105      *
106      * <p>
107      * This method sets the reader up for use. Essentially it checks that
108      * its been invoked in a HTTP-POST environment, reads some optional
109      * configuration variables, and obtains several component references to
110      * be used later.
111      * </p>
112      *
113      * @param resolver <code>SourceResolver</code> instance
114      * @param objectModel request/response/context data
115      * @param src source <code>String</code> instance
116      * @param parameters sitemap invocation time customization parameters
117      * @exception ProcessingException if an error occurs
118      * @exception IOException if an error occurs
119      * @exception SAXException if an error occurs
120      */

121     public void setup(
122         final SourceResolver resolver,
123         final Map JavaDoc objectModel,
124         final String JavaDoc src,
125         final Parameters parameters)
126     throws ProcessingException, IOException JavaDoc, SAXException JavaDoc {
127         super.setup(resolver, objectModel, src, parameters);
128
129         checkHTTPPost(objectModel);
130
131         if (getLogger().isDebugEnabled()) {
132             getLogger().debug("AxisRPCReader.setup() complete");
133         }
134     }
135
136     /**
137      * Helper method to ensure that given a HTTP-POST.
138      *
139      * @param objectModel Request/Response/Context map.
140      * @exception ProcessingException if a non HTTP-POST request has been made.
141      */

142     private void checkHTTPPost(final Map JavaDoc objectModel)
143     throws ProcessingException {
144         String JavaDoc method = ObjectModelHelper.getRequest(objectModel).getMethod();
145
146         if (!"POST".equalsIgnoreCase(method))
147             throw new ProcessingException(
148                 "Reader only supports HTTP-POST (supplied was " + method + ")"
149             );
150     }
151
152     /**
153      * Axis RPC Router <code>generate</code> method.
154      *
155      * <p>
156      * This method reads the SOAP request in from the input stream, invokes
157      * the requested method and sends the result back to the requestor
158      * </p>
159      *
160      * @exception IOException if an IO error occurs
161      * @exception SAXException if a SAX error occurs
162      * @exception ProcessingException if a processing error occurs
163      */

164     public void generate()
165     throws IOException JavaDoc, SAXException JavaDoc, ProcessingException {
166         HttpServletRequest JavaDoc req =
167             (HttpServletRequest JavaDoc) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
168         HttpServletResponse JavaDoc res =
169             (HttpServletResponse JavaDoc) objectModel.get(HttpEnvironment.HTTP_RESPONSE_OBJECT);
170         ServletContext JavaDoc con =
171             (ServletContext JavaDoc) objectModel.get(HttpEnvironment.HTTP_SERVLET_CONTEXT);
172
173         String JavaDoc soapAction = null;
174         MessageContext msgContext = null;
175         Message responseMsg = null;
176
177         try {
178             res.setBufferSize(1024 * 8); // provide performance boost.
179

180             // Get message context w/ various properties set
181
msgContext = m_server.createMessageContext(req, res, con);
182
183             // Get request message
184
Message requestMsg =
185                 new Message(
186                     req.getInputStream(), false,
187                     req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE),
188                     req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION)
189                 );
190
191             if (getLogger().isDebugEnabled()) {
192                 getLogger().debug("Request message:\n" + messageToString(requestMsg));
193             }
194
195             // Set the request(incoming) message field in the context
196
msgContext.setRequestMessage(requestMsg);
197             String JavaDoc url = HttpUtils.getRequestURL(req).toString();
198             msgContext.setProperty(MessageContext.TRANS_URL, url);
199
200             try {
201                 //
202
// Save the SOAPAction header in the MessageContext bag.
203
// This will be used to tell the Axis Engine which service
204
// is being invoked. This will save us the trouble of
205
// having to parse the Request message - although we will
206
// need to double-check later on that the SOAPAction header
207
// does in fact match the URI in the body.
208
// (is this last stmt true??? (I don't think so - Glen))
209
//
210
soapAction = getSoapAction(req);
211
212                 if (soapAction != null) {
213                     msgContext.setUseSOAPAction(true);
214                     msgContext.setSOAPActionURI(soapAction);
215                 }
216
217                 // Create a Session wrapper for the HTTP session.
218
msgContext.setSession(new AxisHttpSession(req));
219
220                 // Invoke the Axis engine...
221
if(getLogger().isDebugEnabled()) {
222                     getLogger().debug("Invoking Axis Engine");
223                 }
224
225                 m_server.invoke(msgContext);
226
227                 if(getLogger().isDebugEnabled()) {
228                     getLogger().debug("Return from Axis Engine");
229                 }
230
231                 responseMsg = msgContext.getResponseMessage();
232                 if (responseMsg == null) {
233                     //tell everyone that something is wrong
234
throw new Exception JavaDoc("no response message");
235                 }
236             } catch (AxisFault fault) {
237                 if (getLogger().isErrorEnabled()) {
238                     getLogger().error("Axis Fault", fault);
239                 }
240
241                 // log and sanitize
242
processAxisFault(fault);
243                 configureResponseFromAxisFault(res, fault);
244                 responseMsg = msgContext.getResponseMessage();
245                 if (responseMsg == null) {
246                     responseMsg = new Message(fault);
247                 }
248             } catch (Exception JavaDoc e) {
249                 //other exceptions are internal trouble
250
responseMsg = msgContext.getResponseMessage();
251                 res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
252                 if (getLogger().isErrorEnabled()) {
253                     getLogger().error("Error during SOAP call", e);
254                 }
255                 if (responseMsg == null) {
256                     AxisFault fault = AxisFault.makeFault(e);
257                     processAxisFault(fault);
258                     responseMsg = new Message(fault);
259                 }
260             }
261         } catch (AxisFault fault) {
262             if (getLogger().isErrorEnabled()) {
263                 getLogger().error("Axis fault occured while perforing request", fault);
264             }
265             processAxisFault(fault);
266             configureResponseFromAxisFault(res, fault);
267             responseMsg = msgContext.getResponseMessage();
268             if( responseMsg == null) {
269                 responseMsg = new Message(fault);
270             }
271         } catch (Exception JavaDoc e) {
272             throw new ProcessingException("Exception thrown while performing request", e);
273         }
274
275         // Send response back
276
if (responseMsg != null) {
277             if (getLogger().isDebugEnabled()) {
278                 getLogger().debug("Sending response:\n" + messageToString(responseMsg));
279             }
280
281             sendResponse(getProtocolVersion(req), msgContext.getSOAPConstants(), res, responseMsg);
282         }
283
284         if (getLogger().isDebugEnabled()) {
285             getLogger().debug("AxisRPCReader.generate() complete");
286         }
287     }
288
289     /**
290      * routine called whenever an axis fault is caught; where they
291      * are logged and any other business. The method may modify the fault
292      * in the process
293      * @param fault what went wrong.
294      */

295     protected void processAxisFault(AxisFault fault) {
296         //log the fault
297
Element JavaDoc runtimeException = fault.lookupFaultDetail(
298                 Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
299         if (runtimeException != null) {
300             getLogger().info("AxisFault:", fault);
301             //strip runtime details
302
fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
303         } else if (getLogger().isDebugEnabled()) {
304             getLogger().debug("AxisFault:", fault);
305         }
306         //dev systems only give fault dumps
307
if (m_isDevelompent) {
308             //strip out the stack trace
309
fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE);
310         }
311     }
312
313     /**
314      * Configure the servlet response status code and maybe other headers
315      * from the fault info.
316      * @param response response to configure
317      * @param fault what went wrong
318      */

319     private void configureResponseFromAxisFault(HttpServletResponse JavaDoc response,
320                                                 AxisFault fault) {
321         // then get the status code
322
// It's been suggested that a lack of SOAPAction
323
// should produce some other error code (in the 400s)...
324
int status = getHttpServletResponseStatus(fault);
325         if (status == HttpServletResponse.SC_UNAUTHORIZED) {
326             // unauth access results in authentication request
327
// TODO: less generic realm choice?
328
response.setHeader("WWW-Authenticate","Basic realm=\"AXIS\"");
329         }
330         response.setStatus(status);
331     }
332
333     /**
334      * Extract information from AxisFault and map it to a HTTP Status code.
335      *
336      * @param af Axis Fault
337      * @return HTTP Status code.
338      */

339     protected int getHttpServletResponseStatus(AxisFault af) {
340         // This will raise a 401 for both "Unauthenticated" & "Unauthorized"...
341
return af.getFaultCode().getLocalPart().startsWith("Server.Unauth")
342                    ? HttpServletResponse.SC_UNAUTHORIZED
343                    : HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
344     }
345
346     /**
347      * write a message to the response, set appropriate headers for content
348      * type..etc.
349      * @param clientVersion client protocol, one of the HTTPConstants strings
350      * @param res response
351      * @param responseMsg message to write
352      * @throws AxisFault
353      * @throws IOException if the response stream can not be written to
354      */

355     private void sendResponse(
356         final String JavaDoc clientVersion,
357         final SOAPConstants constants,
358         final HttpServletResponse JavaDoc res,
359         final Message responseMsg)
360     throws AxisFault, IOException JavaDoc {
361         if (responseMsg == null) {
362             res.setStatus(HttpServletResponse.SC_NO_CONTENT);
363
364             if (getLogger().isDebugEnabled()) {
365                 getLogger().debug("No axis response, not sending one");
366             }
367         } else {
368             if (getLogger().isDebugEnabled()) {
369                 getLogger().debug("Returned Content-Type:" + responseMsg.getContentType(constants));
370                 getLogger().debug("Returned Content-Length:" + responseMsg.getContentLength());
371             }
372
373             try {
374                 res.setContentType(responseMsg.getContentType(constants));
375                 responseMsg.writeTo(res.getOutputStream());
376             } catch (SOAPException JavaDoc e) {
377                 getLogger().error("Exception sending response", e);
378             }
379         }
380
381         if (!res.isCommitted()) {
382             res.flushBuffer(); // Force it right now.
383
}
384     }
385
386     /**
387      * Extract the SOAPAction header.
388      * if SOAPAction is null then we'll we be forced to scan the body for it.
389      * if SOAPAction is "" then use the URL
390      * @param req incoming request
391      * @return the action
392      * @throws AxisFault
393      */

394     private String JavaDoc getSoapAction(HttpServletRequest JavaDoc req)
395     throws AxisFault {
396         String JavaDoc soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
397
398         if (getLogger().isDebugEnabled()) {
399             getLogger().debug("HEADER_SOAP_ACTION:" + soapAction);
400         }
401
402         //
403
// Technically, if we don't find this header, we should probably fault.
404
// It's required in the SOAP HTTP binding.
405
//
406
if (soapAction == null) {
407             throw new AxisFault(
408                       "Client.NoSOAPAction",
409                       "No SOAPAction header",
410                       null, null
411                   );
412         }
413
414         if (soapAction.length() == 0)
415             soapAction = req.getContextPath(); // Is this right?
416

417         return soapAction;
418     }
419
420     /**
421      * Return the HTTP protocol level 1.1 or 1.0
422      * by derived class.
423      */

424     private String JavaDoc getProtocolVersion(HttpServletRequest JavaDoc req) {
425         String JavaDoc ret = HTTPConstants.HEADER_PROTOCOL_V10;
426         String JavaDoc prot = req.getProtocol();
427         if (prot!= null) {
428             int sindex= prot.indexOf('/');
429             if (-1 != sindex) {
430                 String JavaDoc ver= prot.substring(sindex+1);
431                 if (HTTPConstants.HEADER_PROTOCOL_V11.equals(ver.trim())) {
432                     ret = HTTPConstants.HEADER_PROTOCOL_V11;
433                 }
434             }
435         }
436         return ret;
437     }
438
439     /**
440      * Helper method to convert a <code>Message</code> structure
441      * into a <code>String</code>.
442      *
443      * @param msg a <code>Message</code> value
444      * @return a <code>String</code> value
445      */

446     private String JavaDoc messageToString(final Message msg) {
447         try {
448             OutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
449             msg.writeTo(os);
450             return os.toString();
451         } catch (Exception JavaDoc e) {
452             if (getLogger().isWarnEnabled()) {
453                 getLogger().warn(
454                     "Warning, could not convert message (" + msg + ") into string", e
455                 );
456             }
457
458             return null;
459         }
460     }
461
462     /**
463      * Dispose this reader. Release all held resources.
464      */

465     public void dispose() {
466         manager.release(m_server);
467     }
468 }
469
Popular Tags