View Javadoc

1   /*
2    * $Id: MuleMessageToHttpResponse.java 19500 2010-09-09 17:29:17Z dzapata $
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.http.transformers;
12  
13  import org.mule.api.MuleMessage;
14  import org.mule.api.config.MuleProperties;
15  import org.mule.api.lifecycle.InitialisationException;
16  import org.mule.api.transformer.TransformerException;
17  import org.mule.config.MuleManifest;
18  import org.mule.transformer.AbstractMessageTransformer;
19  import org.mule.transformer.types.DataTypeFactory;
20  import org.mule.transport.NullPayload;
21  import org.mule.transport.http.CookieHelper;
22  import org.mule.transport.http.HttpConnector;
23  import org.mule.transport.http.HttpConstants;
24  import org.mule.transport.http.HttpResponse;
25  import org.mule.transport.http.i18n.HttpMessages;
26  import org.mule.util.StringUtils;
27  
28  import java.io.IOException;
29  import java.text.SimpleDateFormat;
30  import java.util.Collection;
31  import java.util.Date;
32  import java.util.Locale;
33  import java.util.Map;
34  
35  import org.apache.commons.httpclient.Cookie;
36  import org.apache.commons.httpclient.Header;
37  import org.apache.commons.httpclient.HttpVersion;
38  
39  /**
40   * Converts a {@link MuleMessage} into an Http response.
41   */
42  public class MuleMessageToHttpResponse extends AbstractMessageTransformer
43  {
44      public static final String CUSTOM_HEADER_PREFIX = "";
45  
46      private String server;
47  
48      public MuleMessageToHttpResponse()
49      {
50          registerSourceType(DataTypeFactory.OBJECT);
51          setReturnDataType(DataTypeFactory.create(HttpResponse.class));
52      }
53  
54      @Override
55      public void initialise() throws InitialisationException
56      {
57          // When running with the source code, Meta information is not set
58          // so product name and version are not available, hence we hard code
59          if (MuleManifest.getProductName() == null)
60          {
61              server = "Mule/SNAPSHOT";
62          }
63          else
64          {
65              server = MuleManifest.getProductName() + "/" + MuleManifest.getProductVersion();
66          }
67      }
68  
69      @Override
70      public Object transformMessage(MuleMessage msg, String outputEncoding) throws TransformerException
71      {
72          Object src = msg.getPayload();
73  
74          // Note this transformer excepts Null as we must always return a result
75          // from the Http
76          // connector if a response transformer is present
77          if (src instanceof NullPayload)
78          {
79              src = StringUtils.EMPTY;
80          }
81  
82          try
83          {
84              HttpResponse response;
85              if (src instanceof HttpResponse)
86              {
87                  response = (HttpResponse) src;
88              }
89              else
90              {
91                  response = createResponse(src, outputEncoding, msg);
92              }
93  
94              // Ensure there's a content type header
95              if (!response.containsHeader(HttpConstants.HEADER_CONTENT_TYPE))
96              {
97                  response.addHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE,
98                          HttpConstants.DEFAULT_CONTENT_TYPE));
99              }
100 
101             // Ensure there's a content length or transfer encoding header
102             if (!response.containsHeader(HttpConstants.HEADER_CONTENT_LENGTH)
103                     && !response.containsHeader(HttpConstants.HEADER_TRANSFER_ENCODING))
104             {
105                 if (response.hasBody())
106                 {
107                     long len = response.getContentLength();
108                     if (len < 0)
109                     {
110                         if (response.getHttpVersion().lessEquals(HttpVersion.HTTP_1_0))
111                         {
112                             // Ensure that we convert the payload to an in memory representation
113                             // so we don't end up with a chunked response
114                             len = msg.getPayloadAsBytes().length;
115 
116                             response.setBody(msg);
117 
118                             Header header = new Header(HttpConstants.HEADER_CONTENT_LENGTH, Long.toString(len));
119                             response.setHeader(header);
120                         }
121                         else
122                         {
123                             Header header = new Header(HttpConstants.HEADER_TRANSFER_ENCODING, "chunked");
124                             response.addHeader(header);
125                         }
126                     }
127                     else
128                     {
129                         Header header = new Header(HttpConstants.HEADER_CONTENT_LENGTH, Long.toString(len));
130                         response.setHeader(header);
131                     }
132                 }
133                 else
134                 {
135                     Header header = new Header(HttpConstants.HEADER_CONTENT_LENGTH, "0");
136                     response.addHeader(header);
137                 }
138             }
139 
140             // See if the the client explicitly handles connection persistence
141             String connHeader = msg.getOutboundProperty(HttpConstants.HEADER_CONNECTION);
142             if (connHeader != null)
143             {
144                 if (connHeader.equalsIgnoreCase("keep-alive"))
145                 {
146                     response.setKeepAlive(true);
147                 }
148                 if (connHeader.equalsIgnoreCase("close"))
149                 {
150                     response.setKeepAlive(false);
151                 }
152             }
153 
154             final String method = msg.getOutboundProperty(HttpConnector.HTTP_METHOD_PROPERTY);
155             if ("HEAD".equalsIgnoreCase(method))
156             {
157                 // this is a head request, we don't want to send the actual content
158                 response.setBody((MuleMessage) null);
159             }
160             return response;
161         }
162         catch (Exception e)
163         {
164             throw new TransformerException(this, e);
165         }
166 
167     }
168 
169     protected HttpResponse createResponse(Object src, String encoding, MuleMessage msg)
170             throws IOException, TransformerException
171     {
172         HttpResponse response = new HttpResponse();
173 
174         Object tmp = msg.getOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY);
175         int status = HttpConstants.SC_OK;
176 
177         if (tmp != null)
178         {
179             status = Integer.valueOf(tmp.toString());
180         }
181         else if (msg.getExceptionPayload() != null)
182         {
183             status = HttpConstants.SC_INTERNAL_SERVER_ERROR;
184         }
185 
186         String version = msg.getInboundProperty(HttpConnector.HTTP_VERSION_PROPERTY);
187         if (version == null)
188         {
189             version = HttpConstants.HTTP11;
190         }
191         String date = new SimpleDateFormat(HttpConstants.DATE_FORMAT, Locale.US).format(new Date());
192 
193         String contentType = msg.getInboundProperty(HttpConstants.HEADER_CONTENT_TYPE);
194         if (contentType == null)
195         {
196             contentType = msg.getInvocationProperty(HttpConstants.HEADER_CONTENT_TYPE);
197         }
198 
199         // MULE-4047 Don't explicitly set the content-type to a default value here,
200         // otherwise any settings on the servlet/transport will be happily ignored.
201         //if (contentType == null)
202         //{
203         //    contentType = HttpConstants.DEFAULT_CONTENT_TYPE;
204         //
205         //    if (encoding != null)
206         //    {
207         //        contentType += "; charset=" + encoding;
208         //    }
209         //    logger.warn("Content-Type was not set, defaulting to: " + contentType);
210         //}
211 
212         response.setStatusLine(HttpVersion.parse(version), status);
213         if (contentType != null)
214         {
215             response.setHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE, contentType));
216         }
217         response.setHeader(new Header(HttpConstants.HEADER_DATE, date));
218         response.setHeader(new Header(HttpConstants.HEADER_SERVER, server));
219         if (msg.getOutboundProperty(HttpConstants.HEADER_EXPIRES) == null)
220         {
221             response.setHeader(new Header(HttpConstants.HEADER_EXPIRES, date));
222         }
223 
224         String etag = msg.getOutboundProperty(HttpConstants.HEADER_ETAG);
225         if (etag != null)
226         {
227             response.setHeader(new Header(HttpConstants.HEADER_ETAG, etag));
228         }
229         response.setFallbackCharset(encoding);
230 
231         Collection<String> headerNames = HttpConstants.RESPONSE_HEADER_NAMES.values();
232 
233         for (String headerName : headerNames)
234         {
235             if (HttpConstants.HEADER_COOKIE_SET.equals(headerName))
236             {
237                 // TODO This have to be improved. We shouldn't have to look in all
238                 // scopes
239                 Object cookiesObject = msg.getInvocationProperty(headerName);
240                 if (cookiesObject == null)
241                 {
242                     cookiesObject = msg.getOutboundProperty(headerName);
243                 }
244                 if (cookiesObject == null)
245                 {
246                     cookiesObject = msg.getInboundProperty(headerName);
247                 }
248                 Cookie[] arrayOfCookies = CookieHelper.asArrayOfCookies(cookiesObject);
249                 for (Cookie cookie : arrayOfCookies)
250                 {
251                     response.addHeader(new Header(headerName,
252                         CookieHelper.formatCookieForASetCookieHeader(cookie)));
253                 }
254             }
255             else
256             {
257                 String value = msg.getInvocationProperty(headerName);
258                 if (value == null)
259                 {
260                     value = msg.getOutboundProperty(headerName);
261                 }
262                 if (value != null)
263                 {
264                     response.setHeader(new Header(headerName, value));
265                 }
266             }
267         }
268 
269         Map customHeaders = msg.getOutboundProperty(HttpConnector.HTTP_CUSTOM_HEADERS_MAP_PROPERTY);
270         if (customHeaders != null)
271         {
272             throw new TransformerException(HttpMessages.customHeaderMapDeprecated(), this);
273         }
274 
275         //attach the outbound properties to the message
276         for (String headerName : msg.getOutboundPropertyNames())
277         {
278             Object v = msg.getOutboundProperty(headerName);
279             if (v != null)
280             {
281                 response.setHeader(new Header(headerName, v.toString()));
282             }
283         }
284 
285         // Mule properties
286         String user = msg.getOutboundProperty(MuleProperties.MULE_USER_PROPERTY);
287         if (user != null)
288         {
289             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_USER_PROPERTY, user));
290         }
291         if (msg.getCorrelationId() != null)
292         {
293             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_CORRELATION_ID_PROPERTY,
294                     msg.getCorrelationId()));
295             response.setHeader(new Header(CUSTOM_HEADER_PREFIX
296                     + MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY,
297                     String.valueOf(msg.getCorrelationGroupSize())));
298             response.setHeader(new Header(CUSTOM_HEADER_PREFIX
299                     + MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY,
300                     String.valueOf(msg.getCorrelationSequence())));
301         }
302         if (msg.getReplyTo() != null)
303         {
304             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_REPLY_TO_PROPERTY,
305                     msg.getReplyTo().toString()));
306         }
307 
308         try
309         {
310             response.setBody(msg);
311         }
312         catch (Exception e)
313         {
314             throw new TransformerException(this, e);
315         }
316 
317         return response;
318     }
319 
320     @Override
321     public boolean isAcceptNull()
322     {
323         return true;
324     }
325 }