View Javadoc

1   /*
2    * $Id: IdempotentReceiver.java 8991 2007-10-08 13:41:54Z holger $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.inbound;
12  
13  import org.mule.config.i18n.CoreMessages;
14  import org.mule.umo.MessagingException;
15  import org.mule.umo.UMOEvent;
16  import org.mule.umo.routing.RoutingException;
17  
18  /**
19   * <code>IdempotentReceiver</code> ensures that only unique messages are received by a
20   * component. It does this by checking the unique ID of the incoming message. Note that
21   * the underlying endpoint must support unique message IDs for this to work, otherwise a
22   * <code>UniqueIdNotSupportedException</code> is thrown.<br>
23   * By default this implementation uses an instance of
24   * {@link IdempotentInMemoryMessageIdStore}.
25   */
26  public class IdempotentReceiver extends SelectiveConsumer
27  {
28      protected volatile IdempotentMessageIdStore idStore;
29      protected volatile String assignedComponentName;
30  
31      // The maximum number of messages to keep in the store; exact interpretation of this
32      // limit is up to the store implementation. By default the store is unbounded.
33      protected volatile int maxMessages = -1;
34  
35      // The number of seconds each message ID is kept in the store;
36      // by default each entry is kept for 5 minutes
37      protected volatile int messageTTL = (60 * 5);
38  
39      // The number of seconds between expiration intervals;
40      // by default we expire every minute
41      protected volatile int expirationInterval = 60;
42  
43      public IdempotentReceiver()
44      {
45          super();
46      }
47  
48      public int getMaxMessages()
49      {
50          return maxMessages;
51      }
52  
53      public void setMaxMessages(int maxMessages)
54      {
55          this.maxMessages = maxMessages;
56      }
57  
58      public int getMessageTTL()
59      {
60          return messageTTL;
61      }
62  
63      public void setMessageTTL(int messageTTL)
64      {
65          this.messageTTL = messageTTL;
66      }
67  
68      public int getExpirationInterval()
69      {
70          return expirationInterval;
71      }
72  
73      public void setExpirationInterval(int expirationInterval)
74      {
75          if (expirationInterval <= 0)
76          {
77              throw new IllegalArgumentException(CoreMessages.propertyHasInvalidValue("expirationInterval",
78                  new Integer(expirationInterval)).toString());
79          }
80  
81          this.expirationInterval = expirationInterval;
82      }
83  
84      protected void initialize(UMOEvent event) throws RoutingException
85      {
86          if (assignedComponentName == null && idStore == null)
87          {
88              this.assignedComponentName = event.getComponent().getDescriptor().getName();
89              this.idStore = this.createMessageIdStore();
90          }
91      }
92  
93      protected IdempotentMessageIdStore createMessageIdStore()
94      {
95          return new IdempotentInMemoryMessageIdStore(assignedComponentName, maxMessages, messageTTL,
96              expirationInterval);
97      }
98  
99      // @Override
100     public boolean isMatch(UMOEvent event) throws MessagingException
101     {
102         if (idStore == null)
103         {
104             // we need to load this on the first request as we need the component name
105             synchronized (this)
106             {
107                 this.initialize(event);
108             }
109         }
110 
111         try
112         {
113             return !idStore.containsId(this.getIdForEvent(event));
114         }
115         catch (Exception ex)
116         {
117             throw new RoutingException(event.getMessage(), event.getEndpoint(), ex);
118         }
119     }
120 
121     // @Override
122     public UMOEvent[] process(UMOEvent event) throws MessagingException
123     {
124         String eventComponentName = event.getComponent().getDescriptor().getName();
125         if (!assignedComponentName.equals(eventComponentName))
126         {
127             IllegalArgumentException iex = new IllegalArgumentException(
128                 "This receiver is assigned to component: " + assignedComponentName
129                                 + " but has received an event for component: " + eventComponentName
130                                 + ". Please check your config to make sure each component"
131                                 + "has its own instance of IdempotentReceiver.");
132             throw new RoutingException(event.getMessage(), event.getEndpoint(), iex);
133         }
134 
135         Object id = this.getIdForEvent(event);
136 
137         try
138         {
139             if (idStore.storeId(id))
140             {
141                 return new UMOEvent[]{event};
142             }
143             else
144             {
145                 return null;
146             }
147         }
148         catch (Exception e)
149         {
150             throw new RoutingException(CoreMessages.failedToWriteMessageToStore(id, assignedComponentName),
151                 event.getMessage(), event.getEndpoint(), e);
152         }
153     }
154 
155     protected Object getIdForEvent(UMOEvent event) throws MessagingException
156     {
157         return event.getMessage().getUniqueId();
158     }
159 
160 }