1
2
3
4
5
6
7
8
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.config.MuleProperties;
17 import org.mule.api.construct.FlowConstruct;
18 import org.mule.api.construct.FlowConstructAware;
19 import org.mule.api.expression.ExpressionManager;
20 import org.mule.api.lifecycle.Initialisable;
21 import org.mule.api.lifecycle.InitialisationException;
22 import org.mule.api.routing.RoutingException;
23 import org.mule.api.store.ObjectAlreadyExistsException;
24 import org.mule.api.store.ObjectStore;
25 import org.mule.api.store.ObjectStoreManager;
26 import org.mule.config.i18n.CoreMessages;
27 import org.mule.processor.AbstractFilteringMessageProcessor;
28 import org.mule.util.concurrent.ThreadNameHelper;
29 import org.mule.util.store.InMemoryObjectStore;
30
31 import java.text.MessageFormat;
32
33
34
35
36
37
38
39
40
41
42
43 public class IdempotentMessageFilter extends AbstractFilteringMessageProcessor implements FlowConstructAware, Initialisable
44 {
45 protected volatile ObjectStore<String> store;
46 protected FlowConstruct flowConstruct;
47 protected String storePrefix;
48
49 protected String idExpression = MessageFormat.format("{0}message:id{1}",
50 ExpressionManager.DEFAULT_EXPRESSION_PREFIX, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX);
51
52 protected String valueExpression = MessageFormat.format("{0}message:id{1}",
53 ExpressionManager.DEFAULT_EXPRESSION_PREFIX, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX);
54
55 public IdempotentMessageFilter()
56 {
57 super();
58 }
59
60 @Override
61 public void initialise() throws InitialisationException
62 {
63 if (storePrefix == null)
64 {
65 storePrefix = String.format("%s.%s.%s", ThreadNameHelper.getPrefix(muleContext),
66 (flowConstruct == null ? "" : flowConstruct.getName()), this.getClass().getName());
67 }
68 if (store == null)
69 {
70 this.store = createMessageIdStore();
71 }
72 }
73
74 protected ObjectStore<String> createMessageIdStore() throws InitialisationException
75 {
76 ObjectStoreManager objectStoreManager = (ObjectStoreManager) muleContext.getRegistry().get(
77 MuleProperties.OBJECT_STORE_MANAGER);
78 return objectStoreManager.getObjectStore(storePrefix, false, -1, 60 * 5 * 1000, 6000 );
79 }
80
81 @Override
82 protected MuleEvent processNext(MuleEvent event) throws MuleException
83 {
84 String id = getIdForEvent(event);
85 String value = getValueForEvent(event);
86 try
87 {
88 try
89 {
90 store.store(id, value);
91 }
92 catch (ObjectAlreadyExistsException ex)
93 {
94
95 logger.debug("Possible duplicate message due to race condition: " + id);
96 }
97 return super.processNext(event);
98 }
99 catch (Exception e)
100 {
101 throw new RoutingException(CoreMessages.failedToWriteMessageToStore(id, storePrefix),
102 event, this, e);
103 }
104 }
105
106 protected String getValueForEvent(MuleEvent event) throws MessagingException
107 {
108 return event.getMuleContext().getExpressionManager().parse(valueExpression, event.getMessage(), true);
109 }
110
111 protected String getIdForEvent(MuleEvent event) throws MessagingException
112 {
113 return event.getMuleContext().getExpressionManager().parse(idExpression, event.getMessage(), true);
114 }
115
116 public String getIdExpression()
117 {
118 return idExpression;
119 }
120
121 public void setIdExpression(String idExpression)
122 {
123 this.idExpression = idExpression;
124 }
125
126 public ObjectStore<String> getStore()
127 {
128 return store;
129 }
130
131 public void setStore(ObjectStore<String> store)
132 {
133 this.store = store;
134 }
135
136 @Override
137 protected boolean accept(MuleEvent event)
138 {
139 return event != null && acceptMessageForFlowConstruct(event) && isNewMessage(event);
140 }
141
142 protected boolean acceptMessageForFlowConstruct(MuleEvent event)
143 {
144 if (flowConstruct.getName().equals(event.getFlowConstruct().getName()))
145 {
146 return true;
147 }
148 else
149 {
150 logger.error("This IdempotentMessageFilter was configured on the service: "
151 + storePrefix + " but has received an event for service: "
152 + flowConstruct.getName() + ". Please check your config to make sure each service"
153 + "has its own instance of IdempotentMessageFilter.");
154 return false;
155 }
156 }
157
158 protected boolean isNewMessage(MuleEvent event)
159 {
160 try
161 {
162 String id = this.getIdForEvent(event);
163 if (store == null)
164 {
165 synchronized (this)
166 {
167 initialise();
168 }
169 }
170 return !store.contains(id);
171 }
172 catch (MuleException e)
173 {
174 logger.error("Exception attempting to determine idempotency of incoming message for "
175 + event.getFlowConstruct().getName() + " from the endpoint "
176 + event.getMessageSourceURI(), e);
177 return false;
178 }
179 }
180
181 @Override
182 public void setFlowConstruct(FlowConstruct flowConstruct)
183 {
184 this.flowConstruct = flowConstruct;
185 }
186
187 public String getValueExpression()
188 {
189 return valueExpression;
190 }
191
192 public void setValueExpression(String valueExpression)
193 {
194 this.valueExpression = valueExpression;
195 }
196
197 public void setStorePrefix(String storePrefix)
198 {
199 this.storePrefix = storePrefix;
200 }
201 }