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.MessageExchangePattern;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleException;
12  import org.mule.api.construct.FlowConstruct;
13  import org.mule.api.endpoint.EndpointMessageProcessorChainFactory;
14  import org.mule.api.endpoint.EndpointURI;
15  import org.mule.api.endpoint.ImmutableEndpoint;
16  import org.mule.api.lifecycle.Disposable;
17  import org.mule.api.processor.MessageProcessor;
18  import org.mule.api.processor.MessageProcessorChain;
19  import org.mule.api.retry.RetryPolicyTemplate;
20  import org.mule.api.routing.filter.Filter;
21  import org.mule.api.security.EndpointSecurityFilter;
22  import org.mule.api.security.SecurityFilter;
23  import org.mule.api.transaction.TransactionConfig;
24  import org.mule.api.transformer.Transformer;
25  import org.mule.api.transport.Connector;
26  import org.mule.processor.SecurityFilterMessageProcessor;
27  import org.mule.routing.MessageFilter;
28  import org.mule.util.ClassUtils;
29  
30  import java.net.URI;
31  import java.util.HashMap;
32  import java.util.LinkedList;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.regex.Matcher;
36  import java.util.regex.Pattern;
37  
38  import edu.emory.mathcs.backport.java.util.Collections;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  
43  /**
44   * <code>ImmutableMuleEndpoint</code> describes a Provider in the Mule Server. A
45   * endpoint is a grouping of an endpoint, an endpointUri and a transformer.
46   */
47  public abstract class AbstractEndpoint implements ImmutableEndpoint, Disposable
48  {
49  
50      private static final long serialVersionUID = -1650380871293160973L;
51  
52      public static final String PROPERTY_PROCESS_SYNCHRONOUSLY = "processSynchronously";
53  
54      /**
55       * logger used by this class
56       */
57      protected static final Log logger = LogFactory.getLog(AbstractEndpoint.class);
58  
59      /**
60       * The endpoint used to communicate with the external system
61       */
62      private final Connector connector;
63  
64      /**
65       * The endpointUri on which to send or receive information
66       */
67      private final EndpointURI endpointUri;
68  
69      private final EndpointMessageProcessorChainFactory messageProcessorsFactory;
70  
71      private final List <MessageProcessor> messageProcessors;
72  
73      private final List <MessageProcessor> responseMessageProcessors;
74      
75      private MessageProcessor messageProcessorChain;
76  
77      /**
78       * The name for the endpoint
79       */
80      private final String name;
81  
82      /**
83       * Any additional properties for the endpoint
84       * // TODO This should be final. See MULE-3105
85       * // TODO Shouldn't this be guarded from concurrent writes?
86       */
87      private Map properties = new HashMap();
88  
89      /**
90       * The transaction configuration for this endpoint
91       */
92      private final TransactionConfig transactionConfig;
93  
94      /**
95       * determines whether unaccepted filtered events should be removed from the
96       * source. If they are not removed its up to the Message receiver to handle
97       * recieving the same message again
98       */
99      private final boolean deleteUnacceptedMessages;
100 
101     private final MessageExchangePattern messageExchangePattern;
102     
103     /**
104      * How long to block when performing a remote synchronisation to a remote host.
105      * This property is optional and will be set to the default Synchonous MuleEvent
106      * time out value if not set
107      */
108     private final int responseTimeout;
109 
110     /**
111      * The state that the endpoint is initialised in such as started or stopped
112      */
113     private final String initialState;
114 
115     private final String endpointEncoding;
116 
117     private MuleContext muleContext;
118 
119     protected RetryPolicyTemplate retryPolicyTemplate;
120 
121     private String endpointBuilderName;
122 
123     private final String endpointMimeType;
124 
125     private boolean disableTransportTransformer = false;
126 
127     public AbstractEndpoint(Connector connector,
128                             EndpointURI endpointUri,
129                             String name,
130                             Map properties,
131                             TransactionConfig transactionConfig,
132                             boolean deleteUnacceptedMessages,
133                             MessageExchangePattern messageExchangePattern,
134                             int responseTimeout,
135                             String initialState,
136                             String endpointEncoding,
137                             String endpointBuilderName,
138                             MuleContext muleContext,
139                             RetryPolicyTemplate retryPolicyTemplate,
140                             EndpointMessageProcessorChainFactory messageProcessorsFactory,
141                             List <MessageProcessor> messageProcessors,
142                             List <MessageProcessor> responseMessageProcessors,
143                             boolean disableTransportTransformer,
144                             String endpointMimeType)
145     {
146         this.connector = connector;
147         this.endpointUri = endpointUri;
148         this.name = name;
149         // TODO Properties should be immutable. See MULE-3105
150         // this.properties = Collections.unmodifiableMap(properties);
151         this.properties.putAll(properties);
152         this.transactionConfig = transactionConfig;
153         this.deleteUnacceptedMessages = deleteUnacceptedMessages;
154 
155         this.responseTimeout = responseTimeout;
156         this.initialState = initialState;
157         this.endpointEncoding = endpointEncoding;
158         this.endpointBuilderName = endpointBuilderName;
159         this.muleContext = muleContext;
160         this.retryPolicyTemplate = retryPolicyTemplate;
161         this.endpointMimeType = endpointMimeType;
162         this.disableTransportTransformer = disableTransportTransformer;
163 
164         if (transactionConfig != null && transactionConfig.getFactory() != null &&
165             transactionConfig.getAction() != TransactionConfig.ACTION_NONE &&
166             transactionConfig.getAction() != TransactionConfig.ACTION_NEVER)
167         {
168             if (logger.isDebugEnabled())
169             {
170                 logger.debug("Endpoint has a transaction configuration. Defaulting to REQUEST_RESPONSE. Endpoint is: " + toString());
171             }
172             this.messageExchangePattern = MessageExchangePattern.REQUEST_RESPONSE;
173         }
174         else
175         {
176             this.messageExchangePattern = messageExchangePattern;
177         }
178 
179         this.messageProcessorsFactory = messageProcessorsFactory;
180         if (messageProcessors == null)
181         {
182             this.messageProcessors = Collections.emptyList();
183         }
184         else
185         {
186             this.messageProcessors = messageProcessors;
187         }
188         if (responseMessageProcessors == null)
189         {
190             this.responseMessageProcessors = Collections.emptyList();
191         }
192         else
193         {
194             this.responseMessageProcessors = responseMessageProcessors;
195         }
196     }
197 
198     public EndpointURI getEndpointURI()
199     {
200         return endpointUri;
201     }
202 
203     public String getAddress()
204     {
205         EndpointURI uri = getEndpointURI();
206         if (uri != null)
207         {
208             return uri.getUri().toString();
209         }
210         else
211         {
212             return null;
213         }
214     }
215 
216     public String getEncoding()
217     {
218         return endpointEncoding;
219     }
220 
221     public String getMimeType()
222     {
223         return endpointMimeType;
224     }
225 
226     public Connector getConnector()
227     {
228         return connector;
229     }
230 
231     public String getName()
232     {
233         return name;
234     }
235 
236     public EndpointMessageProcessorChainFactory getMessageProcessorsFactory()
237     {
238         return messageProcessorsFactory;
239     }
240 
241     public List <MessageProcessor> getMessageProcessors()
242     {
243         return messageProcessors;
244     }
245 
246     public List <MessageProcessor> getResponseMessageProcessors()
247     {
248         return responseMessageProcessors;
249     }
250 
251     /** @deprecated use getMessageProcessors() */
252     public List<Transformer> getTransformers()
253     {
254         return getTransformersFromProcessorList(messageProcessors);
255     }
256 
257     public Map getProperties()
258     {
259         return properties;
260     }
261 
262     public boolean isReadOnly()
263     {
264         return true;
265     }
266 
267     @Override
268     public String toString()
269     {
270         // Use the interface to retrieve the string and set
271         // the endpoint uri to a default value
272         String sanitizedEndPointUri = null;
273         URI uri = null;
274         if (endpointUri != null)
275         {
276             sanitizedEndPointUri = endpointUri.toString();
277             uri = endpointUri.getUri();
278         }
279         // The following will further sanitize the endpointuri by removing
280         // the embedded password. This will only remove the password if the
281         // uri contains all the necessary information to successfully rebuild the url
282         if (uri != null && (uri.getRawUserInfo() != null) && (uri.getScheme() != null) && (uri.getHost() != null)
283                 && (uri.getRawPath() != null))
284         {
285             // build a pattern up that matches what we need tp strip out the password
286             Pattern sanitizerPattern = Pattern.compile("(.*):.*");
287             Matcher sanitizerMatcher = sanitizerPattern.matcher(uri.getRawUserInfo());
288             if (sanitizerMatcher.matches())
289             {
290                 sanitizedEndPointUri = new StringBuffer(uri.getScheme()).append("://")
291                         .append(sanitizerMatcher.group(1))
292                         .append(":<password>")
293                         .append("@")
294                         .append(uri.getHost())
295                         .append(uri.getRawPath())
296                         .toString();
297             }
298             if (uri.getRawQuery() != null)
299             {
300                 sanitizedEndPointUri = sanitizedEndPointUri + "?" + uri.getRawQuery();
301             }
302 
303         }
304 
305         return ClassUtils.getClassName(getClass()) + "{endpointUri=" + sanitizedEndPointUri + ", connector="
306                 + connector + ",  name='" + name + "', mep=" + messageExchangePattern + ", properties=" + properties
307                 + ", transactionConfig=" + transactionConfig + ", deleteUnacceptedMessages=" + deleteUnacceptedMessages
308                 + ", initialState=" + initialState + ", responseTimeout="
309                 + responseTimeout + ", endpointEncoding=" + endpointEncoding + ", disableTransportTransformer="
310                 + disableTransportTransformer + "}";
311     }
312 
313     public String getProtocol()
314     {
315         return connector.getProtocol();
316     }
317 
318     public TransactionConfig getTransactionConfig()
319     {
320         return transactionConfig;
321     }
322 
323     protected static boolean equal(Object a, Object b)
324     {
325         return ClassUtils.equal(a, b);
326     }
327 
328     @Override
329     public boolean equals(Object obj)
330     {
331         if (this == obj)
332         {
333             return true;
334         }
335         if (obj == null || getClass() != obj.getClass())
336         {
337             return false;
338         }
339 
340         final AbstractEndpoint other = (AbstractEndpoint) obj;
341         return equal(retryPolicyTemplate, other.retryPolicyTemplate)
342                 && equal(connector, other.connector)
343                 && deleteUnacceptedMessages == other.deleteUnacceptedMessages
344                 && equal(endpointEncoding, other.endpointEncoding)
345                 && equal(endpointUri, other.endpointUri)
346                 && equal(initialState, other.initialState)
347                 // don't include lifecycle state as lifecycle code includes hashing
348                 // && equal(initialised, other.initialised)
349                 && equal(messageExchangePattern, other.messageExchangePattern)
350                 && equal(name, other.name) 
351                 && equal(properties, other.properties)
352                 && responseTimeout == other.responseTimeout
353                 && equal(messageProcessors, other.messageProcessors)
354                 && equal(responseMessageProcessors, other.responseMessageProcessors)
355                 && equal(transactionConfig, other.transactionConfig)
356                 && disableTransportTransformer == other.disableTransportTransformer;
357     }
358 
359     @Override
360     public int hashCode()
361     {
362         return ClassUtils.hash(new Object[]{this.getClass(), retryPolicyTemplate, connector,
363                 deleteUnacceptedMessages ? Boolean.TRUE : Boolean.FALSE,
364                 endpointEncoding,
365                 endpointUri,
366                 initialState,
367                 // don't include lifecycle state as lifecycle code includes hashing
368                 // initialised,
369                 messageExchangePattern,
370                 name,
371                 properties, 
372                 Integer.valueOf(responseTimeout),
373                 responseMessageProcessors,
374                 transactionConfig,
375                 messageProcessors,
376                 disableTransportTransformer ? Boolean.TRUE : Boolean.FALSE});
377     }
378 
379     public Filter getFilter()
380     {
381         // Call the first MessageFilter in the chain "the filter".
382         for (MessageProcessor mp : messageProcessors)
383         {
384             if (mp instanceof MessageFilter)
385             {
386                 return ((MessageFilter) mp).getFilter();
387             }
388         }
389         return null;
390     }
391 
392     public boolean isDeleteUnacceptedMessages()
393     {
394         return deleteUnacceptedMessages;
395     }
396 
397     /**
398      * Returns an EndpointSecurityFilter for this endpoint. If one is not set, there
399      * will be no authentication on events sent via this endpoint
400      *
401      * @return EndpointSecurityFilter responsible for authenticating message flow via
402      *         this endpoint.
403      * @see org.mule.api.security.EndpointSecurityFilter
404      */
405     public EndpointSecurityFilter getSecurityFilter()
406     {
407         for (MessageProcessor mp : messageProcessors)
408         {
409             if (mp instanceof SecurityFilterMessageProcessor)
410             {
411                 SecurityFilter filter = ((SecurityFilterMessageProcessor)mp).getFilter();
412                 if (filter instanceof EndpointSecurityFilter)
413                 {
414                     return (EndpointSecurityFilter) filter;
415                 }
416             }
417         }
418 
419         return null;
420     }
421 
422     public MessageExchangePattern getExchangePattern()
423     {
424         return messageExchangePattern;
425     }
426 
427     /**
428      * The timeout value for remoteSync invocations
429      *
430      * @return the timeout in milliseconds
431      */
432     public int getResponseTimeout()
433     {
434         return responseTimeout;
435     }
436 
437     /**
438      * Sets the state the endpoint will be loaded in. The States are 'stopped' and
439      * 'started' (default)
440      *
441      * @return the endpoint starting state
442      */
443     public String getInitialState()
444     {
445         return initialState;
446     }
447 
448     /** @deprecated use getResponseMessageProcessors() */
449     public List<Transformer> getResponseTransformers()
450     {
451         return getTransformersFromProcessorList(responseMessageProcessors);
452     }
453 
454     private List<Transformer> getTransformersFromProcessorList(List<MessageProcessor> processors)
455     {
456         List<Transformer> transformers = new LinkedList<Transformer>();
457         for (MessageProcessor processor : processors)
458         {
459             if (processor instanceof Transformer)
460             {
461                 transformers.add((Transformer) processor);
462             }
463             else if (processor instanceof MessageProcessorChain)
464             {
465                 transformers.addAll(getTransformersFromProcessorList(((MessageProcessorChain) processor).getMessageProcessors()));
466             }
467         }
468         return transformers;
469     }
470 
471     public Object getProperty(Object key)
472     {
473         return properties.get(key);
474     }
475 
476     public MuleContext getMuleContext()
477     {
478         return muleContext;
479     }
480 
481     public RetryPolicyTemplate getRetryPolicyTemplate()
482     {
483         return retryPolicyTemplate;
484     }
485 
486     public String getEndpointBuilderName()
487     {
488         return endpointBuilderName;
489     }
490 
491     public boolean isProtocolSupported(String protocol)
492     {
493         return connector.supportsProtocol(protocol);
494     }
495     
496     public boolean isDisableTransportTransformer() 
497     {
498         return disableTransportTransformer;
499     }
500 
501     public void dispose()
502     {
503         this.muleContext = null;
504         //this.messageProcessors.clear();
505         // Don't clear this, since it changes the hash code, which can foul up shutdown processing
506         // when objects have been keyed by endpoint, e.g. dispatchers
507         this.messageProcessorChain = null;
508     }
509 
510     public MessageProcessor getMessageProcessorChain(FlowConstruct flowContruct) throws MuleException
511     {
512         if (messageProcessorChain == null)
513         {
514             messageProcessorChain = createMessageProcessorChain(flowContruct);
515         }
516         return messageProcessorChain;
517     }
518 
519     abstract protected MessageProcessor createMessageProcessorChain(FlowConstruct flowContruct) throws MuleException;
520 }