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.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
31
32
33
34
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 }