View Javadoc

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