View Javadoc

1   /*
2    * $Id: HttpProxy.java 22779 2011-08-29 07:24:54Z pablo.kraan $
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.construct;
12  
13  import org.mule.MessageExchangePattern;
14  import org.mule.api.MuleContext;
15  import org.mule.api.MuleException;
16  import org.mule.api.MuleMessage;
17  import org.mule.api.construct.FlowConstructInvalidException;
18  import org.mule.api.endpoint.InboundEndpoint;
19  import org.mule.api.endpoint.OutboundEndpoint;
20  import org.mule.api.processor.InterceptingMessageProcessor;
21  import org.mule.api.processor.MessageProcessor;
22  import org.mule.api.processor.MessageProcessorChainBuilder;
23  import org.mule.api.source.MessageSource;
24  import org.mule.api.transport.PropertyScope;
25  import org.mule.config.i18n.MessageFactory;
26  import org.mule.construct.AbstractConfigurationPattern;
27  import org.mule.endpoint.DynamicOutboundEndpoint;
28  import org.mule.endpoint.DynamicURIOutboundEndpoint;
29  import org.mule.endpoint.EndpointURIEndpointBuilder;
30  import org.mule.pattern.core.support.CopyInboundToOutboundPropertiesTransformerCallback;
31  import org.mule.processor.ResponseMessageProcessorAdapter;
32  import org.mule.transformer.TransformerTemplate;
33  import org.mule.transformer.TransformerTemplate.TransformerCallback;
34  import org.mule.transformer.simple.MessagePropertiesTransformer;
35  import org.mule.util.ObjectUtils;
36  import org.mule.util.StringUtils;
37  
38  import java.util.List;
39  
40  /**
41   * A simple HTTP proxy that supports transformation and caching.
42   */
43  public class HttpProxy extends AbstractConfigurationPattern
44  {
45      private final OutboundEndpoint outboundEndpoint;
46  
47      private final InterceptingMessageProcessor cachingMessageProcessor;
48  
49      public HttpProxy(final String name,
50                       final MuleContext muleContext,
51                       final MessageSource messageSource,
52                       final OutboundEndpoint outboundEndpoint,
53                       final List<MessageProcessor> transformers,
54                       final List<MessageProcessor> responseTransformers,
55                       final InterceptingMessageProcessor cachingMessageProcessor) throws MuleException
56      {
57          super(name, muleContext, transformers, responseTransformers);
58  
59          if (messageSource == null)
60          {
61              throw new FlowConstructInvalidException(
62                  MessageFactory.createStaticMessage("messageSource can't be null on: " + this.toString()),
63                  this);
64          }
65  
66          super.setMessageSource(messageSource);
67  
68          if (outboundEndpoint == null)
69          {
70              throw new FlowConstructInvalidException(
71                  MessageFactory.createStaticMessage("outboundEndpoint can't be null on: " + this.toString()),
72                  this);
73          }
74  
75          this.outboundEndpoint = outboundEndpoint;
76          this.cachingMessageProcessor = cachingMessageProcessor;
77      }
78  
79      @Override
80      protected void configureMessageProcessorsBeforeTransformation(final MessageProcessorChainBuilder builder)
81      {
82          configureContentLengthRemover(this, builder);
83      }
84  
85      @Override
86      protected void configureMessageProcessorsAfterTransformation(final MessageProcessorChainBuilder builder)
87          throws MuleException
88      {
89          // ensure properties, hence HTTP headers, are propagated both ways
90          final TransformerTemplate copyInboundToOutboundPropertiesTransformer = new TransformerTemplate(
91              new CopyInboundToOutboundPropertiesTransformerCallback());
92          builder.chain(copyInboundToOutboundPropertiesTransformer);
93          builder.chain(new ResponseMessageProcessorAdapter(copyInboundToOutboundPropertiesTransformer));
94  
95          if (cachingMessageProcessor != null)
96          {
97              builder.chain(cachingMessageProcessor);
98          }
99  
100         if (outboundEndpoint instanceof DynamicURIOutboundEndpoint)
101         {
102             // do not mess with endpoints that are already dynamic
103             builder.chain(outboundEndpoint);
104         }
105         else
106         {
107             // create a templated outbound endpoint to propagate extra path elements (including query parameters)
108             builder.chain(new TransformerTemplate(new TransformerCallback()
109             {
110                 public Object doTransform(final MuleMessage message) throws Exception
111                 {
112                     final String pathExtension = StringUtils.substringAfter(
113                         (String) message.getInboundProperty("http.request"),
114                         (String) message.getInboundProperty("http.context.path"));
115 
116                     message.setInvocationProperty("http.path.extension",
117                         StringUtils.defaultString(pathExtension));
118                     return message;
119                 }
120             }));
121 
122             logger.error("Endpoint: " + outboundEndpoint);
123             final DynamicOutboundEndpoint dynamicOutboundEndpoint;
124 
125             if (outboundEndpoint instanceof DynamicOutboundEndpoint)
126             {
127                 dynamicOutboundEndpoint = (DynamicOutboundEndpoint) outboundEndpoint;
128             }
129             else
130             {
131                 final String uriTemplate = outboundEndpoint.getEndpointURI().getUri().toString()
132                                            + "#[variable:http.path.extension]";
133 
134                 dynamicOutboundEndpoint = new DynamicOutboundEndpoint(
135                         new EndpointURIEndpointBuilder(outboundEndpoint), uriTemplate);
136             }
137 
138             builder.chain(dynamicOutboundEndpoint);
139         }
140     }
141 
142     public static void configureContentLengthRemover(final AbstractConfigurationPattern configurationPattern,
143                                                      final MessageProcessorChainBuilder builder)
144     {
145         // if transformers have been configured, preemptively drop the content-length header to prevent side effects
146         // induced by mismatches
147         if ((configurationPattern.hasTransformers()) || (configurationPattern.hasResponseTransformers()))
148         {
149             final MessagePropertiesTransformer contentLengthHeaderRemover = newContentLengthHeaderRemover();
150             if (configurationPattern.hasTransformers())
151             {
152                 builder.chain(contentLengthHeaderRemover);
153             }
154             if (configurationPattern.hasResponseTransformers())
155             {
156                 builder.chain(new ResponseMessageProcessorAdapter(contentLengthHeaderRemover));
157             }
158         }
159     }
160 
161     private static MessagePropertiesTransformer newContentLengthHeaderRemover()
162     {
163         final MessagePropertiesTransformer contentLengthHeaderRemover = new MessagePropertiesTransformer();
164         contentLengthHeaderRemover.setScope(PropertyScope.INBOUND);
165         contentLengthHeaderRemover.setDeleteProperties("(?i)content-length");
166         return contentLengthHeaderRemover;
167     }
168 
169     @Override
170     protected void validateConstruct() throws FlowConstructInvalidException
171     {
172         super.validateConstruct();
173 
174         if (messageSource instanceof InboundEndpoint)
175         {
176             final InboundEndpoint inboundEndpoint = (InboundEndpoint) messageSource;
177 
178             if (!inboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE))
179             {
180                 throw new FlowConstructInvalidException(
181                     MessageFactory.createStaticMessage("HttpProxy only works with a request-response inbound endpoint."),
182                     this);
183             }
184         }
185 
186         if (!outboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE))
187         {
188             throw new FlowConstructInvalidException(
189                 MessageFactory.createStaticMessage("HttpProxy only works with a request-response outbound endpoint."),
190                 this);
191         }
192     }
193 
194     @Override
195     public String toString()
196     {
197         return ObjectUtils.toString(this);
198     }
199 
200     @Override
201     public String getConstructType()
202     {
203         return "HTTP-Proxy";
204     }
205 }