View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.servlet;
8   
9   import org.mule.RequestContext;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleMessage;
12  import org.mule.api.config.MuleProperties;
13  import org.mule.api.lifecycle.InitialisationException;
14  import org.mule.api.transformer.TransformerException;
15  import org.mule.api.transport.OutputHandler;
16  import org.mule.config.ExceptionHelper;
17  import org.mule.transport.http.HttpConstants;
18  import org.mule.transport.http.HttpResponse;
19  import org.mule.transport.http.transformers.MuleMessageToHttpResponse;
20  
21  import java.io.IOException;
22  
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServlet;
25  import javax.servlet.http.HttpServletResponse;
26  
27  import org.apache.commons.httpclient.Header;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  /**
32   * A base servlet used to receive requests from a servlet container and route
33   * them into Mule
34   */
35  
36  public abstract class AbstractReceiverServlet extends HttpServlet
37  {
38      /**
39       * logger used by this class
40       */
41      protected transient Log logger = LogFactory.getLog(getClass());
42  
43      public static final String REQUEST_TIMEOUT_PROPERTY = "org.mule.servlet.timeout";
44      public static final String FEEDBACK_PROPERTY = "org.mule.servlet.feedback";
45      public static final String DEFAULT_CONTENT_TYPE_PROPERTY = "org.mule.servlet.default.content.type";
46  
47      /**
48       * The name of the servlet connector to use with this Servlet
49       * @deprecated Use {@link org.mule.transport.servlet.MuleServletContextListener#CONNECTOR_NAME} instead
50       */
51      @Deprecated
52      public static final String SERVLET_CONNECTOR_NAME_PROPERTY = "org.mule.servlet.connector.name";
53  
54      public static final String PAYLOAD_PARAMETER_NAME = "org.mule.servlet.payload.param";
55      public static final String DEFAULT_PAYLOAD_PARAMETER_NAME = "payload";
56  
57      public static final long DEFAULT_GET_TIMEOUT = 10000L;
58  
59      protected String payloadParameterName;
60      protected long timeout = DEFAULT_GET_TIMEOUT;
61      protected boolean feedback = true;
62      protected String defaultContentType = HttpConstants.DEFAULT_CONTENT_TYPE;
63      protected MuleContext muleContext;
64  
65      private MuleMessageToHttpResponse responseTransformer = new MuleMessageToHttpResponse();
66  
67      @Override
68      public final void init() throws ServletException
69      {
70          String timeoutString = getServletConfig().getInitParameter(REQUEST_TIMEOUT_PROPERTY);
71          if (timeoutString != null)
72          {
73              timeout = Long.parseLong(timeoutString);
74          }
75          if (logger.isInfoEnabled())
76          {
77              logger.info("Default request timeout for GET methods is: " + timeout);
78          }
79  
80          String feedbackString = getServletConfig().getInitParameter(FEEDBACK_PROPERTY);
81          if (feedbackString != null)
82          {
83              feedback = Boolean.valueOf(feedbackString);
84          }
85          if (logger.isInfoEnabled())
86          {
87              logger.info("feedback is set to: " + feedback);
88          }
89  
90          String ct = getServletConfig().getInitParameter(DEFAULT_CONTENT_TYPE_PROPERTY);
91          if (ct != null)
92          {
93              if (logger.isDebugEnabled())
94              {
95                  logger.debug("Using default content type configured on the servlet (" + DEFAULT_CONTENT_TYPE_PROPERTY + ") = " + ct);
96              }
97              defaultContentType = ct;
98          }
99          if (logger.isInfoEnabled())
100         {
101             logger.info("Default content type is: " + defaultContentType);
102         }
103 
104         payloadParameterName = getServletConfig().getInitParameter(PAYLOAD_PARAMETER_NAME);
105         if (payloadParameterName == null)
106         {
107             payloadParameterName = DEFAULT_PAYLOAD_PARAMETER_NAME;
108         }
109         if (logger.isInfoEnabled())
110         {
111             logger.info("Using payload param name: " + payloadParameterName);
112         }
113 
114         muleContext = setupMuleContext();
115         setupResponseTransformer();
116         doInit();
117     }
118 
119     protected MuleContext setupMuleContext() throws ServletException
120     {
121         MuleContext context = (MuleContext) getServletContext().getAttribute(MuleProperties.MULE_CONTEXT_PROPERTY);
122         if (context == null)
123         {
124             throw new ServletException("Property " + MuleProperties.MULE_CONTEXT_PROPERTY + " not set on ServletContext");
125         }
126         return context;
127     }
128 
129     protected void setupResponseTransformer() throws ServletException
130     {
131         responseTransformer.setMuleContext(muleContext);
132 
133         try
134         {
135             responseTransformer.initialise();
136         }
137         catch (InitialisationException e)
138         {
139             throw new ServletException(e);
140         }
141     }
142 
143     protected void doInit() throws ServletException
144     {
145         // template method
146     }
147 
148     protected void writeResponse(HttpServletResponse servletResponse, MuleMessage message) throws Exception
149     {
150         if (message == null)
151         {
152             writeEmptyResponse(servletResponse);
153         }
154         else
155         {
156             writeResponseFromMessage(servletResponse, message);
157         }
158         servletResponse.flushBuffer();
159     }
160 
161     protected void writeEmptyResponse(HttpServletResponse servletResponse) throws IOException
162     {
163         servletResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);
164         if (feedback)
165         {
166             servletResponse.setStatus(HttpServletResponse.SC_OK);
167             servletResponse.getWriter().write("Action was processed successfully. There was no result");
168         }
169     }
170 
171     protected void writeResponseFromMessage(HttpServletResponse servletResponse, MuleMessage message) throws Exception
172     {
173         HttpResponse httpResponse = convertToHttpResponse(message);
174         setHttpHeadersOnServletResponse(httpResponse, servletResponse);
175 
176         if (!servletResponse.isCommitted())
177         {
178             servletResponse.setStatus(httpResponse.getStatusCode());
179         }
180 
181         if (httpResponse.hasBody())
182         {
183             OutputHandler outputHandler = httpResponse.getBody();
184             outputHandler.write(RequestContext.getEvent(), servletResponse.getOutputStream());
185         }
186     }
187 
188     protected HttpResponse convertToHttpResponse(MuleMessage message) throws TransformerException
189     {
190         if (message.getPayload() instanceof HttpResponse)
191         {
192             return (HttpResponse) message.getPayload();
193 
194         }
195         else
196         {
197             return (HttpResponse) responseTransformer.transform(message);
198         }
199     }
200 
201     protected HttpServletResponse setHttpHeadersOnServletResponse(HttpResponse httpResponse, HttpServletResponse servletResponse)
202     {
203         // Remove any Transfer-Encoding headers that were set (e.g. by MuleMessageToHttpResponse)
204         // earlier. Mule's default HTTP transformer is used in both cases: when the reply
205         // MuleMessage is generated for our standalone HTTP server and for the servlet case. The
206         // servlet container should be able to figure out the Transfer-Encoding itself and some
207         // get confused by an existing header.
208         httpResponse.removeHeaders(HttpConstants.HEADER_TRANSFER_ENCODING);
209 
210         Header[] headers = httpResponse.getHeaders();
211         for (Header header : headers)
212         {
213             servletResponse.addHeader(header.getName(), header.getValue());
214         }
215 
216         ensureContentTypeHeaderIsSet(servletResponse, httpResponse);
217 
218         return servletResponse;
219     }
220 
221     protected void ensureContentTypeHeaderIsSet(HttpServletResponse servletResponse, HttpResponse httpResponse)
222     {
223         Header contentTypeHeader = httpResponse.getFirstHeader(HttpConstants.HEADER_CONTENT_TYPE);
224         String contentType = defaultContentType;
225         if (contentTypeHeaderIsValid(contentTypeHeader))
226         {
227             if (logger.isDebugEnabled())
228             {
229                 logger.debug("Using Content-Type from message header = " + contentTypeHeader.getValue());
230             }
231             contentType = contentTypeHeader.getValue();
232         }
233         servletResponse.setContentType(contentType);
234     }
235 
236     protected boolean contentTypeHeaderIsValid(Header header)
237     {
238         return (header != null) && (header.getValue() != null);
239     }
240 
241     protected void handleException(Throwable exception, String message, HttpServletResponse response)
242     {
243         logger.error("message: " + exception.getMessage(), exception);
244         int code = Integer.valueOf(ExceptionHelper.getErrorMapping("http", exception.getClass()));
245         response.setStatus(code);
246         try
247         {
248             response.sendError(code, message + ": " + exception.getMessage());
249         }
250         catch (IOException e)
251         {
252             logger.error("Failed to sendError on response: " + e.getMessage(), e);
253         }
254     }
255 }