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.endpoint;
8   
9   import org.mule.DefaultMuleEvent;
10  import org.mule.MessageExchangePattern;
11  import org.mule.api.MuleContext;
12  import org.mule.api.MuleEvent;
13  import org.mule.api.MuleException;
14  import org.mule.api.MuleMessage;
15  import org.mule.api.endpoint.EndpointBuilder;
16  import org.mule.api.endpoint.EndpointException;
17  import org.mule.api.endpoint.EndpointMessageProcessorChainFactory;
18  import org.mule.api.endpoint.EndpointURI;
19  import org.mule.api.endpoint.MalformedEndpointException;
20  import org.mule.api.endpoint.OutboundEndpoint;
21  import org.mule.api.expression.ExpressionManager;
22  import org.mule.api.expression.ExpressionRuntimeException;
23  import org.mule.api.lifecycle.InitialisationException;
24  import org.mule.api.processor.MessageProcessor;
25  import org.mule.api.retry.RetryPolicyTemplate;
26  import org.mule.api.routing.filter.Filter;
27  import org.mule.api.security.EndpointSecurityFilter;
28  import org.mule.api.transaction.TransactionConfig;
29  import org.mule.api.transformer.Transformer;
30  import org.mule.api.transport.Connector;
31  import org.mule.api.transport.DispatchException;
32  import org.mule.config.i18n.CoreMessages;
33  import org.mule.transport.AbstractConnector;
34  import org.mule.util.ObjectNameHelper;
35  
36  import java.util.Collections;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.Properties;
40  
41  import org.apache.commons.collections.map.LRUMap;
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  
45  /**
46   * An Outbound endpoint who's URI is a template used to created new non dynamic
47   * endpoints based on the current message.
48   * This allows for the destination of a message to change based on the contents
49   * of the message. Note that this endpoint ONLY substitutes the URI, but other
50   * config elements such as the transformers, filters, etc do not change. You
51   * cannot change an endpoint scheme dynamically so you can't switch between
52   * HTTP and JMS for example using the same dynamic endpoint.
53   */
54  public class DynamicOutboundEndpoint implements OutboundEndpoint
55  {
56  
57      public static final String DYNAMIC_URI_PLACEHOLDER = "dynamic://endpoint";
58  
59      protected transient final Log logger = LogFactory.getLog(DynamicOutboundEndpoint.class);
60  
61      private static final long serialVersionUID = 8861985949279708638L;
62  
63      /**
64       * The URI template used to construct the actual URI to send the message to.
65       */
66      protected String uriTemplate;
67  
68      private final EndpointBuilder builder;
69  
70      private final OutboundEndpoint prototypeEndpoint;
71  
72      // Caches resolved static endpoints to improve performance
73      private Map<String, OutboundEndpoint> staticEndpoints = Collections.synchronizedMap(new LRUMap(64));
74  
75      public DynamicOutboundEndpoint(EndpointBuilder builder, String uriTemplate) throws MalformedEndpointException
76      {
77          validateUriTemplate(uriTemplate);
78  
79          this.builder = builder;
80          this.uriTemplate = uriTemplate;
81  
82          try
83          {
84              prototypeEndpoint = builder.buildOutboundEndpoint();
85          }
86          catch (Exception e)
87          {
88              throw new RuntimeException(e);
89          }
90      }
91  
92      protected void validateUriTemplate(String uri) throws MalformedEndpointException
93      {
94          if (uri.indexOf(":") > uri.indexOf(ExpressionManager.DEFAULT_EXPRESSION_PREFIX))
95          {
96              throw new MalformedEndpointException(CoreMessages.dynamicEndpointsMustSpecifyAScheme(), uri);
97          }
98      }
99  
100     protected EndpointURI getEndpointURIForMessage(MuleEvent event) throws DispatchException
101     {
102         if (logger.isDebugEnabled())
103         {
104             logger.debug("Uri before parsing is: " + uriTemplate);
105         }
106 
107         String newUriString = uriTemplate;
108         try
109         {
110             newUriString = parseURIString(newUriString, event.getMessage());
111         }
112         catch (final ExpressionRuntimeException e)
113         {
114             throw new DispatchException(event, this, e);
115         }
116 
117         if (logger.isDebugEnabled())
118         {
119             logger.debug("Uri after parsing is: " + newUriString);
120         }
121 
122         try
123         {
124             final MuleEndpointURI resultUri = new MuleEndpointURI(newUriString, getMuleContext(), getServiceOverrides());
125             resultUri.initialise();
126 
127             return resultUri;
128         }
129         catch (final Exception e)
130         {
131             throw new DispatchException(CoreMessages.templateCausedMalformedEndpoint(uriTemplate, newUriString), event, this, e);
132         }
133     }
134 
135     private Properties getServiceOverrides() throws EndpointException
136     {
137         Properties properties = null;
138 
139         if (builder instanceof AbstractEndpointBuilder)
140         {
141             Connector connector = ((AbstractEndpointBuilder) this.builder).getConnector();
142 
143             if (connector instanceof AbstractConnector && ((AbstractConnector) connector).getServiceOverrides() != null)
144             {
145                 properties = new Properties();
146                 properties.putAll(((AbstractConnector) connector).getServiceOverrides());
147             }
148         }
149 
150         return properties;
151     }
152 
153     protected String parseURIString(String uri, MuleMessage message)
154     {
155         return this.getMuleContext().getExpressionManager().parse(uri, message, true);
156     }
157 
158     public MuleEvent process(MuleEvent event) throws MuleException
159     {
160         EndpointURI endpointURIForMessage = getEndpointURIForMessage(event);
161         OutboundEndpoint outboundEndpoint = getStaticEndpointFor(endpointURIForMessage);
162 
163         event = new DefaultMuleEvent(event.getMessage(), outboundEndpoint, event, event.getSession());
164 
165         return outboundEndpoint.process(event);
166     }
167 
168     private OutboundEndpoint getStaticEndpointFor(EndpointURI uri) throws EndpointException, InitialisationException
169     {
170         OutboundEndpoint outboundEndpoint = staticEndpoints.get(uri.getAddress());
171 
172         if (outboundEndpoint == null)
173         {
174             outboundEndpoint = createStaticEndpoint(uri);
175             staticEndpoints.put(uri.getAddress(), outboundEndpoint);
176         }
177 
178         return outboundEndpoint;
179     }
180 
181     private OutboundEndpoint createStaticEndpoint(EndpointURI uri) throws EndpointException, InitialisationException
182     {
183         try
184         {
185             EndpointBuilder staticBuilder = (EndpointBuilder) builder.clone();
186             staticBuilder.setURIBuilder(new URIBuilder(uri));
187             String endpointName = ObjectNameHelper.getEndpointNameFor(uri);
188             staticBuilder.setName(endpointName);
189             return staticBuilder.buildOutboundEndpoint();
190         }
191         catch (CloneNotSupportedException e)
192         {
193             // This cannot happen, because we implement Cloneable
194             throw new IllegalStateException("Unable to clone endpoint builder");
195         }
196     }
197 
198     @Override
199     public boolean equals(Object o)
200     {
201         return this == o;
202     }
203 
204     @Override
205     public int hashCode()
206     {
207         return System.identityHashCode(this);
208     }
209 
210     public Connector getConnector()
211     {
212         throw new UnsupportedOperationException("No connector available");
213     }
214 
215     public EndpointURI getEndpointURI()
216     {
217         return null;
218     }
219 
220     public String getAddress()
221     {
222         return uriTemplate;
223     }
224 
225     public RetryPolicyTemplate getRetryPolicyTemplate()
226     {
227         return prototypeEndpoint.getRetryPolicyTemplate();
228     }
229 
230     public String getEncoding()
231     {
232         return prototypeEndpoint.getEncoding();
233     }
234 
235     public String getMimeType()
236     {
237         return prototypeEndpoint.getMimeType();
238     }
239 
240     public Filter getFilter()
241     {
242         return prototypeEndpoint.getFilter();
243     }
244 
245     public String getInitialState()
246     {
247         return prototypeEndpoint.getInitialState();
248     }
249 
250     public MuleContext getMuleContext()
251     {
252         return prototypeEndpoint.getMuleContext();
253     }
254 
255     public String getName()
256     {
257         return prototypeEndpoint.getName();
258     }
259 
260     public Map getProperties()
261     {
262         return prototypeEndpoint.getProperties();
263     }
264 
265     public Object getProperty(Object key)
266     {
267         return prototypeEndpoint.getProperty(key);
268     }
269 
270     public String getProtocol()
271     {
272         return prototypeEndpoint.getProtocol();
273     }
274 
275     public int getResponseTimeout()
276     {
277         return prototypeEndpoint.getResponseTimeout();
278     }
279 
280     public List<Transformer> getResponseTransformers()
281     {
282         return prototypeEndpoint.getResponseTransformers();
283     }
284 
285     public EndpointMessageProcessorChainFactory getMessageProcessorsFactory()
286     {
287         return prototypeEndpoint.getMessageProcessorsFactory();
288     }
289 
290     public List<MessageProcessor> getMessageProcessors()
291     {
292         return prototypeEndpoint.getMessageProcessors();
293     }
294 
295     public List<MessageProcessor> getResponseMessageProcessors()
296     {
297         return prototypeEndpoint.getResponseMessageProcessors();
298     }
299 
300     public EndpointSecurityFilter getSecurityFilter()
301     {
302         return prototypeEndpoint.getSecurityFilter();
303     }
304 
305     public TransactionConfig getTransactionConfig()
306     {
307         return prototypeEndpoint.getTransactionConfig();
308     }
309 
310     public List<Transformer> getTransformers()
311     {
312         return prototypeEndpoint.getTransformers();
313     }
314 
315     public boolean isDeleteUnacceptedMessages()
316     {
317         return prototypeEndpoint.isDeleteUnacceptedMessages();
318     }
319 
320     public boolean isReadOnly()
321     {
322         return prototypeEndpoint.isReadOnly();
323     }
324 
325     public MessageExchangePattern getExchangePattern()
326     {
327         return prototypeEndpoint.getExchangePattern();
328     }
329 
330     public List<String> getResponseProperties()
331     {
332         return prototypeEndpoint.getResponseProperties();
333     }
334 
335     public String getEndpointBuilderName()
336     {
337         return prototypeEndpoint.getEndpointBuilderName();
338     }
339 
340     public boolean isProtocolSupported(String protocol)
341     {
342         return prototypeEndpoint.isProtocolSupported(protocol);
343     }
344 
345     public boolean isDisableTransportTransformer()
346     {
347         return prototypeEndpoint.isDisableTransportTransformer();
348     }
349 }