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