View Javadoc

1   /*
2    * $Id: UMOMessageToHttpResponse.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers.http.transformers;
12  
13  import org.mule.MuleManager;
14  import org.mule.config.MuleProperties;
15  import org.mule.providers.NullPayload;
16  import org.mule.providers.http.HttpConnector;
17  import org.mule.providers.http.HttpConstants;
18  import org.mule.providers.http.HttpResponse;
19  import org.mule.transformers.AbstractEventAwareTransformer;
20  import org.mule.transformers.simple.SerializableToByteArray;
21  import org.mule.umo.UMOEventContext;
22  import org.mule.umo.UMOMessage;
23  import org.mule.umo.transformer.TransformerException;
24  import org.mule.util.StringUtils;
25  
26  import java.io.ByteArrayInputStream;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.text.SimpleDateFormat;
30  import java.util.Collection;
31  import java.util.Date;
32  import java.util.Iterator;
33  import java.util.Locale;
34  import java.util.Map;
35  
36  import org.apache.commons.httpclient.Header;
37  import org.apache.commons.httpclient.HttpVersion;
38  
39  /**
40   * <code>UMOMessageToHttpResponse</code> converts a UMOMEssage into an Http
41   * response.
42   */
43  
44  public class UMOMessageToHttpResponse extends AbstractEventAwareTransformer
45  {
46      public static final String CUSTOM_HEADER_PREFIX = "";
47  
48      // @GuardedBy("itself")
49      private SimpleDateFormat format;
50      private String server;
51      private SerializableToByteArray serializableToByteArray;
52  
53      public UMOMessageToHttpResponse()
54      {
55          registerSourceType(Object.class);
56          setReturnClass(Object.class);
57  
58          format = new SimpleDateFormat(HttpConstants.DATE_FORMAT, Locale.US);
59  
60          // When running with the source code, Meta information is not set
61          // so product name and version are not available, hence we hard code
62          if (MuleManager.getConfiguration().getProductName() == null)
63          {
64              server = "Mule/SNAPSHOT";
65          }
66          else
67          {
68              server = MuleManager.getConfiguration().getProductName() + "/"
69                       + MuleManager.getConfiguration().getProductVersion();
70          }
71  
72          serializableToByteArray = new SerializableToByteArray();
73      }
74  
75      public Object transform(Object src, String encoding, UMOEventContext context) throws TransformerException
76      {
77          // Send back the exception payload if one has been set
78          if (context.getMessage().getExceptionPayload() != null)
79          {
80              // src = context.getMessage().getExceptionPayload();
81          }
82  
83          // Note this transformer excepts Null as we must always return a result
84          // from the Http
85          // connector if a response transformer is present
86          if (src instanceof NullPayload)
87          {
88              src = StringUtils.EMPTY;
89          }
90  
91          try
92          {
93              HttpResponse response;
94              if (src instanceof HttpResponse)
95              {
96                  response = (HttpResponse)src;
97              }
98              else
99              {
100                 response = createResponse(src, encoding, context);
101             }
102 
103             // Ensure there's a content type header
104             if (!response.containsHeader(HttpConstants.HEADER_CONTENT_TYPE))
105             {
106                 response.addHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE,
107                     HttpConstants.DEFAULT_CONTENT_TYPE));
108             }
109 
110             // Ensure there's a content length or transfer encoding header
111             if (!response.containsHeader(HttpConstants.HEADER_CONTENT_LENGTH)
112                 && !response.containsHeader(HttpConstants.HEADER_TRANSFER_ENCODING))
113             {
114                 InputStream content = response.getBody();
115                 if (content != null)
116                 {
117                     long len = response.getContentLength();
118                     if (len < 0)
119                     {
120                         if (response.getHttpVersion().lessEquals(HttpVersion.HTTP_1_0))
121                         {
122                             throw new IOException("Chunked encoding not supported for HTTP version "
123                                                   + response.getHttpVersion());
124                         }
125                         Header header = new Header(HttpConstants.HEADER_TRANSFER_ENCODING, "chunked");
126                         response.addHeader(header);
127                     }
128                     else
129                     {
130                         Header header = new Header(HttpConstants.HEADER_CONTENT_LENGTH, Long.toString(len));
131                         response.setHeader(header);
132                     }
133                 }
134                 else
135                 {
136                     Header header = new Header(HttpConstants.HEADER_CONTENT_LENGTH, "0");
137                     response.addHeader(header);
138                 }
139             }
140 
141             UMOMessage msg = context.getMessage();
142 
143             if (!response.containsHeader(HttpConstants.HEADER_CONNECTION))
144             {
145                 // See if the the client explicitly handles connection persistence
146                 String connHeader = msg.getStringProperty(HttpConstants.HEADER_CONNECTION, null);
147                 if (connHeader != null)
148                 {
149                     if (connHeader.equalsIgnoreCase("keep-alive"))
150                     {
151                         Header header = new Header(HttpConstants.HEADER_CONNECTION, "keep-alive");
152                         response.addHeader(header);
153                         response.setKeepAlive(true);
154                     }
155                     if (connHeader.equalsIgnoreCase("close"))
156                     {
157                         Header header = new Header(HttpConstants.HEADER_CONNECTION, "close");
158                         response.addHeader(header);
159                         response.setKeepAlive(false);
160                     }
161                 }
162                 else
163                 {
164                     // Use protocol default connection policy
165                     if (response.getHttpVersion().greaterEquals(HttpVersion.HTTP_1_1))
166                     {
167                         response.setKeepAlive(true);
168                     }
169                     else
170                     {
171                         response.setKeepAlive(false);
172                     }
173                 }
174             }
175             if ("HEAD".equalsIgnoreCase(msg.getStringProperty(HttpConnector.HTTP_METHOD_PROPERTY, null)))
176             {
177                 // this is a head request, we don't want to send the actual content
178                 response.setBody(null);
179             }
180             return response;
181         }
182         catch (IOException e)
183         {
184             throw new TransformerException(this, e);
185         }
186 
187     }
188 
189     protected HttpResponse createResponse(Object src, String encoding, UMOEventContext context)
190         throws IOException, TransformerException
191     {
192         HttpResponse response = new HttpResponse();
193         UMOMessage msg = context.getMessage();
194 
195         int status = msg.getIntProperty(HttpConnector.HTTP_STATUS_PROPERTY, HttpConstants.SC_OK);
196         String version = msg.getStringProperty(HttpConnector.HTTP_VERSION_PROPERTY, HttpConstants.HTTP11);
197         
198         String date;
199         synchronized (format)
200         {
201             date = format.format(new Date());
202         }
203 
204         String contentType = msg.getStringProperty(HttpConstants.HEADER_CONTENT_TYPE,
205             HttpConstants.DEFAULT_CONTENT_TYPE);
206 
207         response.setStatusLine(HttpVersion.parse(version), status);
208         response.setHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE, contentType));
209         response.setHeader(new Header(HttpConstants.HEADER_DATE, date));
210         response.setHeader(new Header(HttpConstants.HEADER_SERVER, server));
211         if (msg.getProperty(HttpConstants.HEADER_EXPIRES) == null)
212         {
213             response.setHeader(new Header(HttpConstants.HEADER_EXPIRES, date));
214         }
215         response.setFallbackCharset(encoding);
216 
217         Collection headerNames = HttpConstants.RESPONSE_HEADER_NAMES.values();
218         String headerName, value;
219         for (Iterator iterator = headerNames.iterator(); iterator.hasNext();)
220         {
221             headerName = (String)iterator.next();
222             value = msg.getStringProperty(headerName, null);
223             if (value != null)
224             {
225                 response.setHeader(new Header(headerName, value));
226             }
227         }
228 
229         // Custom responseHeaderNames
230         Map customHeaders = (Map)msg.getProperty(HttpConnector.HTTP_CUSTOM_HEADERS_MAP_PROPERTY);
231         if (customHeaders != null)
232         {
233             Map.Entry entry;
234             for (Iterator iterator = customHeaders.entrySet().iterator(); iterator.hasNext();)
235             {
236                 entry = (Map.Entry)iterator.next();
237                 if (entry.getValue() != null)
238                 {
239                     response.setHeader(new Header(entry.getKey().toString(), entry.getValue().toString()));
240                 }
241             }
242         }
243 
244         // Mule properties
245         String user = msg.getStringProperty(MuleProperties.MULE_USER_PROPERTY, null);
246         if (user != null)
247         {
248             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_USER_PROPERTY, user));
249         }
250         if (msg.getCorrelationId() != null)
251         {
252             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_CORRELATION_ID_PROPERTY,
253                 msg.getCorrelationId()));
254             response.setHeader(new Header(CUSTOM_HEADER_PREFIX
255                                           + MuleProperties.MULE_CORRELATION_GROUP_SIZE_PROPERTY,
256                 String.valueOf(msg.getCorrelationGroupSize())));
257             response.setHeader(new Header(CUSTOM_HEADER_PREFIX
258                                           + MuleProperties.MULE_CORRELATION_SEQUENCE_PROPERTY,
259                 String.valueOf(msg.getCorrelationSequence())));
260         }
261         if (msg.getReplyTo() != null)
262         {
263             response.setHeader(new Header(CUSTOM_HEADER_PREFIX + MuleProperties.MULE_REPLY_TO_PROPERTY,
264                 msg.getReplyTo().toString()));
265         }
266         if (src instanceof InputStream)
267         {
268             response.setBody((InputStream)src);
269         }
270         else if (src instanceof String)
271         {
272             response.setBodyString(src.toString());
273         }
274         else
275         {
276             response.setBody(new ByteArrayInputStream((byte[])serializableToByteArray.doTransform(src,
277                 encoding)));
278         }
279         return response;
280     }
281 
282     public boolean isAcceptNull()
283     {
284         return true;
285     }
286 }