View Javadoc

1   /*
2    * $Id: ServletMuleMessageFactory.java 20112 2010-11-08 01:33:08Z mike.schilling $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.servlet;
12  
13  import org.mule.DefaultMuleMessage;
14  import org.mule.api.MuleContext;
15  import org.mule.api.MuleMessage;
16  import org.mule.transport.AbstractMuleMessageFactory;
17  import org.mule.transport.http.HttpConstants;
18  
19  import java.util.Enumeration;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpSession;
26  
27  import org.apache.commons.collections.EnumerationUtils;
28  
29  public class ServletMuleMessageFactory extends AbstractMuleMessageFactory
30  {
31      private static final String REMOTE_ADDRESS_HEADER = "remoteAddress";
32  
33      public ServletMuleMessageFactory(MuleContext context)
34      {
35          super(context);
36      }
37  
38      @Override
39      protected Class<?>[] getSupportedTransportMessageTypes()
40      {
41          return new Class[] { HttpServletRequest.class };
42      }
43  
44      @Override
45      protected Object extractPayload(Object transportMessage, String encoding) throws Exception
46      {
47          HttpServletRequest request = (HttpServletRequest) transportMessage;
48  
49          String method = request.getMethod();
50          if (HttpConstants.METHOD_GET.equalsIgnoreCase(method))
51          {
52              return queryString(request);
53          }
54          else
55          {
56              return extractPayloadFromPostRequest(request);
57          }
58      }
59  
60      protected Object extractPayloadFromPostRequest(HttpServletRequest request) throws Exception
61      {
62          /*
63           * Servlet Spec v2.5:
64           *
65           * SRV.3.1.1
66           * When Parameters Are Available
67           *
68           * The following are the conditions that must be met before post form data will
69           * be populated to the parameter set:
70           *
71           * 1. The request is an HTTP or HTTPS request.
72           * 2. The HTTP method is POST.
73           * 3. The content type is application/x-www-form-urlencoded.
74           * 4. The servlet has made an initial call of any of the getParameter family of meth-
75           *    ods on the request object.
76           * 
77           * If the conditions are not met and the post form data is not included in the
78           * parameter set, the post data must still be available to the servlet via the request
79           * object's input stream. If the conditions are met, post form data will no longer be
80           * available for reading directly from the request object's input stream.
81           *
82           * -----------------------------------------------------------------------------------
83           *
84           * In plain english:if you call getInputStream on a POSTed request before you call one of
85           * the getParameter* methods, you'll lose the parameters. So we touch the parameters first
86           * and only then we return the input stream that will be the payload of the message.
87           */
88          request.getParameterNames();
89  
90          return request.getInputStream();
91      }
92  
93      protected String queryString(HttpServletRequest request)
94      {
95          StringBuilder buf = new StringBuilder(request.getRequestURI());
96  
97          String queryString = request.getQueryString();
98          if (queryString != null)
99          {
100             buf.append("?");
101             buf.append(queryString);
102         }
103 
104         return buf.toString();
105     }
106 
107     @Override
108     protected void addProperties(DefaultMuleMessage message, Object transportMessage) throws Exception
109     {
110         HttpServletRequest request = (HttpServletRequest) transportMessage;
111 
112         setupRequestParameters(request, message);
113         setupEncoding(request, message);
114         setupSessionId(request, message);
115         setupContentType(request, message);
116         setupCharacterEncoding(request, message);
117         setupRemoteAddress(request, message);
118         setupMessageProperties(request, message);
119     }
120 
121     @SuppressWarnings("unchecked")
122     protected void setupRequestParameters(HttpServletRequest request, DefaultMuleMessage message)
123     {
124         Enumeration<String> parameterNames = request.getParameterNames();
125         if (parameterNames != null)
126         {
127             Map<String, Object> parameterProperties = new HashMap<String, Object>();
128             Map<String, Object> parameterMap = new HashMap<String, Object>();
129             while (parameterNames.hasMoreElements())
130             {
131                 String name = parameterNames.nextElement();
132                 String key = ServletConnector.PARAMETER_PROPERTY_PREFIX + name;
133                 String value = request.getParameterValues(name)[0];
134 
135                 parameterProperties.put(key, value);
136                 parameterMap.put(name, value);
137             }
138 
139             // support for the HttpRequestToParameterMap transformer: put the map of request
140             // parameters under a well defined key into the message properties as well
141             parameterProperties.put(ServletConnector.PARAMETER_MAP_PROPERTY_KEY, parameterMap);
142 
143             message.addInboundProperties(parameterProperties);
144         }
145     }
146 
147     protected void setupEncoding(HttpServletRequest request, MuleMessage message)
148     {
149         String contentType = request.getContentType();
150         if (contentType != null)
151         {
152             int index = contentType.indexOf("charset");
153             if (index > -1)
154             {
155                 int semicolonIndex = contentType.lastIndexOf(";");
156                 if (semicolonIndex > index)
157                 {
158                     message.setEncoding(contentType.substring(index + 8, semicolonIndex));
159                 }
160                 else
161                 {
162                     message.setEncoding(contentType.substring(index + 8));
163                 }
164             }
165         }
166     }
167 
168     protected void setupSessionId(HttpServletRequest request, MuleMessage message)
169     {
170         try
171         {
172             // We wrap this call as on some App Servers (Websphere) it can cause an NPE
173             HttpSession session = request.getSession(false);
174             if (session != null)
175             {
176                 ((DefaultMuleMessage) message).setInboundProperty(ServletConnector.SESSION_ID_PROPERTY_KEY, session.getId());
177             }
178         }
179         catch (Exception e)
180         {
181             // C'est la vie
182         }
183     }
184 
185     protected void setupContentType(HttpServletRequest request, DefaultMuleMessage message)
186     {
187         String contentType = request.getContentType();
188         
189         Map<String, Object> properties = new HashMap<String, Object>();
190         properties.put(ServletConnector.CONTENT_TYPE_PROPERTY_KEY, contentType);
191      
192         message.addInboundProperties(properties);
193     }
194     
195     protected void setupCharacterEncoding(HttpServletRequest request, DefaultMuleMessage message)
196     {
197         String characterEncoding = request.getCharacterEncoding();
198         
199         Map<String, Object> properties = new HashMap<String, Object>();
200         properties.put(ServletConnector.CHARACTER_ENCODING_PROPERTY_KEY, characterEncoding);
201      
202         message.addInboundProperties(properties);
203     }
204     
205     protected void setupRemoteAddress(HttpServletRequest request, DefaultMuleMessage message)
206     {
207         message.setInboundProperty(REMOTE_ADDRESS_HEADER, request.getRemoteAddr());
208     }
209 
210     protected void setupMessageProperties(HttpServletRequest request, DefaultMuleMessage message)
211     {
212         Map<String, Object> messageProperties = new HashMap<String, Object>();
213         
214         copyParameters(request, messageProperties);
215         copyAttributes(request, messageProperties);
216         copyHeaders(request, messageProperties);
217         
218         message.addInboundProperties(messageProperties);
219     }
220 
221     protected void copyParameters(HttpServletRequest request, Map<String, Object> messageProperties)
222     {
223         Map<?, ?> parameterMap = request.getParameterMap();
224         if (parameterMap != null && parameterMap.size() > 0)
225         {
226             for (Map.Entry<?, ?> entry : parameterMap.entrySet())
227             {
228                 String key = (String) entry.getKey();
229                 Object value = entry.getValue();
230                 if (value != null)
231                 {
232                     if (value.getClass().isArray() && ((Object[]) value).length == 1)
233                     {
234                         value = ((Object[]) value)[0];
235                     }
236                 }
237                 
238                 messageProperties.put(key, value);
239             }
240         }
241     }
242 
243     protected void copyAttributes(HttpServletRequest request, Map<String, Object> messageProperties)
244     {
245         for (Enumeration<?> e = request.getAttributeNames(); e.hasMoreElements();)
246         {
247             String key = (String) e.nextElement();
248             messageProperties.put(key, request.getAttribute(key));
249         }
250     }
251     
252     protected void copyHeaders(HttpServletRequest request, Map<String, Object> messageProperties)
253     {
254         for (Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements();)
255         {
256             String key = (String)e.nextElement();
257             String realKey = key;
258             
259             if (key.startsWith(HttpConstants.X_PROPERTY_PREFIX))
260             {
261                 realKey = key.substring(2);
262             }
263 
264             // Workaround for containers that strip the port from the Host header.
265             // This is needed so Mule components can figure out what port they're on.
266             if (HttpConstants.HEADER_HOST.equalsIgnoreCase(key))
267             {
268                 realKey = HttpConstants.HEADER_HOST;
269 
270                 String value = request.getHeader(key);
271                 int port = request.getLocalPort();
272                 if (!value.contains(":") && port != 80 && port != 443)
273                 {
274                     value = value + ":" + port;
275                 }
276                 messageProperties.put(realKey, value);
277             }
278             else
279             {
280                 Enumeration<?> valueEnum = request.getHeaders(key);
281                 List<?> values = EnumerationUtils.toList(valueEnum);
282                 if (values.size() > 1)
283                 {
284                     messageProperties.put(realKey, values.toArray());
285                 }
286                 else
287                 {
288                     messageProperties.put(realKey, values.get(0));
289                 }
290             }
291         }
292     }
293 }