View Javadoc

1   /*
2    * $Id: IdempotentMessageFilter.java 20320 2010-11-24 15:03:31Z dfeist $
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.routing;
12  
13  import org.mule.api.MessagingException;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleException;
16  import org.mule.api.construct.FlowConstruct;
17  import org.mule.api.construct.FlowConstructAware;
18  import org.mule.api.expression.ExpressionManager;
19  import org.mule.api.lifecycle.Initialisable;
20  import org.mule.api.lifecycle.InitialisationException;
21  import org.mule.api.routing.RoutingException;
22  import org.mule.api.store.ObjectStore;
23  import org.mule.config.i18n.CoreMessages;
24  import org.mule.processor.AbstractFilteringMessageProcessor;
25  import org.mule.util.store.InMemoryObjectStore;
26  
27  import java.text.MessageFormat;
28  
29  /**
30   * <code>IdempotentMessageFilter</code> ensures that only unique messages are passed on. It does this by
31   * checking the unique ID of the incoming message. Note that the underlying endpoint must support unique
32   * message IDs for this to work, otherwise a <code>UniqueIdNotSupportedException</code> is thrown.<br>
33   * <p>
34   * <b>EIP Reference:</b> <a href="http://www.eaipatterns.com/IdempotentReceiver.html">http://www.eaipatterns.com/IdempotentReceiver.html</a>
35   */
36  public class IdempotentMessageFilter extends AbstractFilteringMessageProcessor implements FlowConstructAware, Initialisable
37  {
38      protected volatile ObjectStore<String> store;
39      protected volatile String assignedComponentName;
40      protected FlowConstruct flowConstruct;
41  
42      protected String idExpression = MessageFormat.format("{0}message:id{1}",
43          ExpressionManager.DEFAULT_EXPRESSION_PREFIX, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX);
44  
45      public IdempotentMessageFilter()
46      {
47          super();
48      }
49  
50      public void initialise() throws InitialisationException
51      {
52          if (store == null)
53          {
54              this.store = this.createMessageIdStore();
55          }
56      }
57  
58      protected ObjectStore<String> createMessageIdStore() throws InitialisationException
59      {
60          InMemoryObjectStore<String> s = new InMemoryObjectStore<String>();
61          s.setName(assignedComponentName);
62          s.setMaxEntries(-1);
63          s.setEntryTTL(60 * 5 * 1000);
64          s.setExpirationInterval(6000);
65          s.initialise();
66          return s;
67      }
68  
69      @Override
70      protected MuleEvent processNext(MuleEvent event) throws MuleException
71      {
72          String id = this.getIdForEvent(event);
73          try
74          {
75              store.store(id, id);
76              return super.processNext(event);
77          }
78          catch (Exception e)
79          {
80              throw new RoutingException(CoreMessages.failedToWriteMessageToStore(id, assignedComponentName),
81                  event, this, e);
82          }
83      }
84  
85      protected String getIdForEvent(MuleEvent event) throws MessagingException
86      {
87          return event.getMuleContext().getExpressionManager().parse(idExpression, event.getMessage(), true);
88      }
89  
90      public String getIdExpression()
91      {
92          return idExpression;
93      }
94  
95      public void setIdExpression(String idExpression)
96      {
97          this.idExpression = idExpression;
98      }
99  
100     public ObjectStore<String> getStore()
101     {
102         return store;
103     }
104 
105     public void setStore(ObjectStore<String> store)
106     {
107         this.store = store;
108     }
109 
110     @Override
111     protected boolean accept(MuleEvent event)
112     {
113         return event != null && acceptMessageForFlowConstruct(event) && isNewMessage(event);
114     }
115 
116     protected boolean acceptMessageForFlowConstruct(MuleEvent event)
117     {
118         if (flowConstruct.getName().equals(event.getFlowConstruct().getName()))
119         {
120             return true;
121         }
122         else
123         {
124             logger.error("This IdempotentMessageFilter was configured on the service: "
125                          + assignedComponentName + " but has received an event for service: "
126                          + flowConstruct.getName() + ". Please check your config to make sure each service"
127                          + "has its own instance of IdempotentMessageFilter.");
128             return false;
129         }
130     }
131 
132     protected boolean isNewMessage(MuleEvent event)
133     {
134         try
135         {
136             String id = this.getIdForEvent(event);
137             if (store == null)
138             {
139                 synchronized (this)
140                 {
141                     initialise();
142                 }
143             }
144             return !store.contains(id);
145         }
146         catch (MuleException e)
147         {
148             logger.error("Exception attempting to determine idempotency of incoming message for "
149                          + event.getFlowConstruct().getName() + " from the endpoint "
150                          + event.getEndpoint().getEndpointURI().getUri(), e);
151             return false;
152         }
153     }
154 
155     public void setFlowConstruct(FlowConstruct flowConstruct)
156     {
157         this.flowConstruct = flowConstruct;
158     }
159 
160 }