View Javadoc

1   /*
2    * Copyright (c) 2004 UNINETT FAS
3    *
4    * This program is free software; you can redistribute it and/or modify it
5    * under the terms of the GNU General Public License as published by the Free
6    * Software Foundation; either version 2 of the License, or (at your option)
7    * any later version.
8    *
9    * This program is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12   * more details.
13   *
14   * You should have received a copy of the GNU General Public License along with
15   * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16   * Place - Suite 330, Boston, MA 02111-1307, USA.
17   *
18   * $Id: SimpleAxisServlet.java,v 1.7 2006/01/09 10:44:29 catoolsen Exp $
19   */
20  
21  package no.feide.moria.servlet.soap;
22  
23  import java.io.ByteArrayOutputStream;
24  import java.io.IOException;
25  import java.io.PrintWriter;
26  import java.rmi.RemoteException;
27  
28  import javax.servlet.RequestDispatcher;
29  import javax.servlet.ServletException;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  
33  import no.feide.moria.log.MessageLogger;
34  
35  import org.apache.axis.AxisEngine;
36  import org.apache.axis.AxisFault;
37  import org.apache.axis.Constants;
38  import org.apache.axis.Message;
39  import org.apache.axis.MessageContext;
40  import org.apache.axis.handlers.soap.SOAPService;
41  import org.apache.axis.transport.http.AxisHttpSession;
42  import org.apache.axis.transport.http.AxisServlet;
43  import org.apache.axis.transport.http.HTTPConstants;
44  import org.apache.axis.transport.http.ServletEndpointContextImpl;
45  import org.apache.xml.serialize.XMLSerializer;
46  import org.w3c.dom.Document;
47  
48  /***
49   * @author Bjørn Ola Smievoll <b.o@smievoll.no>
50   * @version $Revision: 1.7 $
51   */
52  public final class SimpleAxisServlet
53  extends AxisServlet {
54  
55      /***
56       * Serial version UID.
57       */
58      private static final long serialVersionUID = 6202683940363529171L;
59      
60      /*** 
61       * Logger for this class.
62       * */
63      private MessageLogger messageLogger = new MessageLogger(SimpleAxisServlet.class);
64  
65  
66      /***
67       * Default constructor.
68       */
69      public SimpleAxisServlet() {
70  
71          super();
72      }
73  
74  
75      /***
76       * Initializes the servlet. Called by the container.
77       * @throws ServletException
78       *             If unable to initialize the servlet.
79       */
80      public void init() throws ServletException {
81  
82          super.init();
83      }
84  
85  
86      /***
87       * Handles HTTP GET requests. As SOAP uses POST, this basically returns a
88       * empty page.
89       * @param request
90       *            The incoming HTTP request object.
91       * @param response
92       *            The outgoing HTTP reponse object.
93       */
94      public void doGet(final HttpServletRequest request, final HttpServletResponse response) {
95  
96          /* Avoid null-pointer exceptions. */
97          if (request == null) {
98              messageLogger.logCritical("Response object cannot be null", new NullPointerException("request is null"));
99              return;
100         }
101 
102         if (response == null) {
103             messageLogger.logCritical("Response object cannot be null", new NullPointerException("response is null"));
104             return;
105         }
106 
107         /* The access point to the web services */
108         AxisEngine axisEngine = null;
109 
110         /* The object representing the requested web service */
111         SOAPService service = null;
112 
113         /* Writer for response */
114         PrintWriter printWriter = null;
115 
116         /* Get writer for the WSDL output (XML) */
117         try {
118             printWriter = response.getWriter();
119         } catch (IOException ioe) {
120             handleException("Unable to get response writer", ioe, request, response);
121             return;
122         }
123 
124         /* Retrieve axis engine */
125         try {
126             axisEngine = getEngine();
127         } catch (AxisFault fault) {
128             handleException("Unable to get AxisEngine", fault, request, response);
129             return;
130         }
131 
132         /* Context used by the axis engine to handle the request */
133         MessageContext messageContext = createMessageContext(axisEngine, request, response);
134 
135         /* Set the username given in the request. */
136         messageContext.setUsername(request.getRemoteUser());
137 
138         /* Identify service. */
139         String serviceName = request.getServletPath();
140 
141         try {
142             service = axisEngine.getService(serviceName);
143         } catch (AxisFault fault) {
144             handleException("Unable to get SOAPService", fault, request, response);
145             return;
146         }
147 
148         /* Make NullPointerException page and return if service is null */
149         if (service == null) {
150             handleException("No SOAPService object returned", new NullPointerException("service is null"), request, response);
151             return;
152         }
153 
154         /* Add service to message context */
155         try {
156             messageContext.setService(service);
157         } catch (AxisFault af) {
158             handleException("Unable to set SOAPService in messageContext", af, request, response);
159             return;
160         }
161 
162         /* Identify type of response (HTML or WSDL) */
163         String queryString = request.getQueryString();
164 
165         /*
166          * Return WSDL if correct query string has been given, otherwise return
167          * simple HTML page
168          */
169         if (queryString != null && queryString.equalsIgnoreCase("wsdl")) {
170 
171             /* XML document object to hold the returned WSDL data */
172             Document wsdl = null;
173 
174             /* Get the service to generate the WSDL */
175             try {
176                 service.generateWSDL(messageContext);
177             } catch (AxisFault af) {
178                 handleException("Unable to generate WSDL", af, request, response);
179                 return;
180             }
181 
182             /* The generated XML is put into the context */
183             Object object = messageContext.getProperty("WSDL");
184             if (object instanceof Document)
185                 wsdl = (Document) object;
186 
187             /* Make exception page and return if the WSDL data was not generated */
188             if (wsdl == null) {
189                 handleException("No WSDL data available", new NullPointerException("wsdl is null"), request, response);
190                 return;
191             }
192 
193             /* Serialize DOM to XML string */
194             ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
195             XMLSerializer xmlSerializer = new XMLSerializer();
196             xmlSerializer.setOutputByteStream(outputStream);
197 
198             /* Log and return if serialization fails */
199             try {
200                 xmlSerializer.serialize(wsdl);
201             } catch (IOException ioe) {
202                 handleException("Unable to serialize WSDL", ioe, request, response);
203                 return;
204             }
205 
206             /* Print result to client */
207             response.setCharacterEncoding("UTF-8");
208             response.setContentType("text/xml; charset=UTF-8");
209             printWriter.print(outputStream.toString());
210 
211         } else {
212             /* Get JSP for handling HTML output */
213             RequestDispatcher requestDispatcher = request.getSession().getServletContext().getNamedDispatcher("Axis.JSP");
214 
215             /* Set the service name as attribute for the JSP */
216             request.setAttribute("serviceName", serviceName);
217 
218             /* Log and return if dispatch fails */
219             try {
220                 requestDispatcher.forward(request, response);
221             } catch (Exception e) {
222                 //handleException("Unable to dispatch request to " +
223                 // jspLocation + "/Axis.JSP", e, request, response);
224                 return;
225             }
226         }
227     }
228 
229 
230     /***
231      * Handles HTTP POST requests. This method does the real work, handling the
232      * SOAP requests.
233      * @param request
234      *            The incoming HTTP request object.
235      * @param response
236      *            The outgoing HTTP response object.
237      */
238     public void doPost(final HttpServletRequest request, final HttpServletResponse response) {
239 
240         /* Avoid null-pointer exceptions. */
241         if (request == null) {
242             messageLogger.logCritical("Response object cannot be null", new NullPointerException("request is null"));
243             return;
244         }
245 
246         if (response == null) {
247             messageLogger.logCritical("Response object cannot be null", new NullPointerException("response is null"));
248             return;
249         }
250 
251         /* Supposed to boost performace. */
252         response.setBufferSize(8192);
253 
254         /* The access point to the AXIS subsystem. */
255         AxisEngine axisEngine = null;
256 
257         /* The objecte representing the incoming SOAP message. */
258         Message requestMessage = null;
259 
260         /* The object representing the requested web service. */
261         SOAPService service = null;
262 
263         /* Retrieve instance of AXIS engine. */
264         try {
265             axisEngine = getEngine();
266         } catch (AxisFault fault) {
267             handleException("Unable to get AxisEngine", fault, request, response);
268             return;
269         }
270 
271         /*
272          * Create message context used by the AXIS subsystem to handle the
273          * request.
274          */
275         MessageContext messageContext = createMessageContext(axisEngine, request, response);
276 
277         /* Set the username given in the request. */
278         messageContext.setUsername(request.getRemoteUser());
279 
280         /* Identify service. */
281         String serviceName = request.getServletPath();
282 
283         /*
284          * Retrieve SOAP service object. Make NullPointerException page and
285          * return if SOAP service is null.
286          */
287         try {
288             service = axisEngine.getService(serviceName);
289         } catch (AxisFault fault) {
290             handleException("Unable to get SOAPService", fault, request, response);
291             return;
292         }
293 
294         if (service == null) {
295             handleException("No SOAPService object returned", new NullPointerException("service is null"), request, response);
296             return;
297         }
298 
299         /* Add the SOAP service to message context. */
300         try {
301             messageContext.setService(service);
302         } catch (AxisFault af) {
303             handleException("Unable to set SOAPService in messageContext", af, request, response);
304             return;
305         }
306 
307         /*
308          * Create request message. Log and return if we can't get input stream
309          * from request.
310          */
311         try {
312             requestMessage = new Message(request.getInputStream(), false, request.getHeader("Content-Type"), request.getHeader("Content-Location"));
313         } catch (IOException ioe) {
314             handleException("Unable to get InputStream from request", ioe, request, response);
315             return;
316         }
317 
318         /* Add the request message to the message context. */
319         messageContext.setRequestMessage(requestMessage);
320 
321         /* Get the SOAP action header from the HTTP request. */
322         String soapAction = request.getHeader("SOAPAction");
323 
324         /*
325          * If the SoapAction header is undefined we set the variable equal to
326          * the request uri.
327          */
328         if (soapAction == null)
329             soapAction = request.getRequestURI();
330 
331         /* Add the SOAP action to the message context. */
332         messageContext.setUseSOAPAction(true);
333         messageContext.setSOAPActionURI(soapAction);
334 
335         /* Create session wrapper for the HTTP session. */
336         messageContext.setSession(new AxisHttpSession(request));
337 
338         /*
339          * Invoke the engine and thereby process the request, log and return if
340          * it fails.
341          */
342         try {
343             axisEngine.invoke(messageContext);
344         } catch (AxisFault af) {
345             
346             if ((af.getFaultString().startsWith("No such operation")) ||
347                 (af.getFaultString().startsWith("org.xml.sax.SAXException"))) {
348                 
349                 // Handle exceptions caused by illegal input.
350                 IllegalInputException e = new IllegalInputException(af.getFaultString());
351                 handleException("Invocation of the Axis engine failed", e, request, response);
352                               
353             } else
354                 
355                 // All other Axis exceptions.
356                 handleException("Invocation of the axis engine failed", af, request, response);
357 
358             return;
359         }
360 
361         /* Read response message from engine. */
362         Message responseMessage = messageContext.getResponseMessage();
363 
364         /* Log and return if no response. */
365         if (responseMessage == null) {
366             handleException("No response from engine", new NullPointerException("responseMessage is null"), request, response);
367             return;
368         }
369 
370         /*
371          * If we're unable to retrieve the content type, set it to a default
372          * value.
373          */
374         try {
375             response.setContentType(responseMessage.getContentType(messageContext.getSOAPConstants()));
376         } catch (AxisFault af) {
377             handleException("Unable to retrieve content type of response", af, request, response);
378             // TODO: Verify default content type for SOAP messages
379             response.setContentType("application/soap+xml");
380         }
381 
382         /* Write message to client */
383         try {
384             responseMessage.writeTo(response.getOutputStream());
385         } catch (Exception e) {
386             handleException("Unable to write response to client", e, request, response);
387             return;
388         }
389 
390     }
391 
392 
393     /***
394      * Creates a new MessageContext, initialized with some standard values.
395      * @param axisEngine
396      *            The AxisEngine that will be used to handle SOAP operations.
397      * @param request
398      *            The incoming request.
399      * @param response
400      *            The outgoing response.
401      * @return An initialized MessageContext.
402      */
403     private MessageContext createMessageContext(final AxisEngine axisEngine, final HttpServletRequest request, final HttpServletResponse response) {
404 
405         /* Create new message context */
406         MessageContext messageContext = new MessageContext(axisEngine);
407 
408         /* Set the transport */
409         messageContext.setTransportName("transport.name");
410 
411         /* Save some HTTP specific info in the bag in case someone needs it */
412         messageContext.setProperty(Constants.MC_RELATIVE_PATH, request.getServletPath());
413         messageContext.setProperty(Constants.MC_REMOTE_ADDR, request.getRemoteAddr());
414         messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLET, this);
415         messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, request);
416         messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, response);
417         messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO, request.getPathInfo());
418         messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLETLOCATION, getWebInfPath());
419         messageContext.setProperty(HTTPConstants.HEADER_AUTHORIZATION, request.getHeader(HTTPConstants.HEADER_AUTHORIZATION));
420 
421         /* Set up a javax.xml.rpc.server.ServletEndpointContext. */
422         ServletEndpointContextImpl endpointContext = new ServletEndpointContextImpl();
423         messageContext.setProperty(Constants.MC_SERVLET_ENDPOINT_CONTEXT, endpointContext);
424 
425         /* Save the real path. */
426         String realPath = getServletContext().getRealPath(request.getServletPath());
427 
428         if (realPath != null)
429             messageContext.setProperty(Constants.MC_REALPATH, realPath);
430 
431         /* Set config path. */
432         messageContext.setProperty(Constants.MC_CONFIGPATH, getWebInfPath());
433 
434         /* Axis voodoo. */
435         messageContext.setProperty(MessageContext.TRANS_URL, request.getRequestURL().toString());
436 
437         return messageContext;
438     }
439 
440 
441     /***
442      * Logs exception with message and prints user friendly error message to
443      * client.
444      * @param message
445      *            Message to be logged with the exception.
446      * @param exception
447      *            The exception to be handled.
448      * @param request
449      *            Request object for this invocation.
450      * @param response
451      *            Response object for this invocation.
452      */
453     private void handleException(final String message, final Exception exception, final HttpServletRequest request, final HttpServletResponse response) {
454 
455         /* We're not able to recover from this */
456         response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
457 
458         /* Get the exception cause. */
459         Throwable cause = null;
460         if (exception instanceof AxisFault)
461             cause = exception.getCause();
462         else
463             cause = exception;
464 
465         /* Used later to get the JSP for handling HTML output. */
466         RequestDispatcher requestDispatcher;
467 
468         /* Set up the error response specially for SOAP requests. */
469         if (request.getMethod().equals("POST") && request.getHeader("SOAPAction") != null) {
470 
471             /*
472              * Set the faultCode and faultString elements, depending on
473              * exception.
474              */
475             if (cause instanceof SOAPException) {
476 
477                 /* A known type of exception. */
478                 request.setAttribute("faultCode", ((SOAPException) cause).getFaultcode());
479                 request.setAttribute("faultString", ((SOAPException) cause).getFaultstring());
480 
481             } else if (cause instanceof RemoteException) {
482 
483                 // An older type of exception handling, replaced by the v2.1
484                 // SOAP interface onwards.
485                 request.setAttribute("faultCode", "Server");
486                 request.setAttribute("faultString", cause.getMessage());
487 
488             } else {
489 
490                 /* An unknown type of exception. Should not happen. */
491                 InternalException internal = new InternalException("");
492                 request.setAttribute("faultCode", internal.getFaultcode());
493                 request.setAttribute("faultString", internal.getFaultstring());
494                 messageLogger.logCritical("Unknown internal exception", cause);
495 
496             }
497 
498             /* Log what we're doing. */
499             messageLogger.logWarn("Replying with SOAP Fault - faultCode: '" + request.getAttribute("faultCode") + "' faultString: '" + request.getAttribute("faultString") + "'", cause);
500 
501             /* Get the request dispatcher. */
502             requestDispatcher = request.getSession().getServletContext().getNamedDispatcher("Axis-SOAP-Error.JSP");
503 
504         } else {
505 
506             /* Not a SOAP request? */
507             request.setAttribute("logMessage", message);
508             requestDispatcher = request.getSession().getServletContext().getNamedDispatcher("Axis-Error.JSP");
509 
510         }
511 
512         /* Log and return if dispatch fails */
513         try {
514             requestDispatcher.forward(request, response);
515         } catch (Exception e) {
516             messageLogger.logCritical("Unable to dispatch to Axis error jsp", e);
517             return;
518         }
519     }
520 }