1
2
3
4
5
6
7 package org.mule.routing;
8
9 import org.mule.api.MessagingException;
10 import org.mule.api.MuleEvent;
11 import org.mule.api.MuleException;
12 import org.mule.api.construct.FlowConstruct;
13 import org.mule.api.construct.FlowConstructAware;
14 import org.mule.api.expression.ExpressionManager;
15 import org.mule.api.lifecycle.Initialisable;
16 import org.mule.api.lifecycle.InitialisationException;
17 import org.mule.api.routing.RoutingException;
18 import org.mule.api.store.ObjectStore;
19 import org.mule.config.i18n.CoreMessages;
20 import org.mule.processor.AbstractFilteringMessageProcessor;
21 import org.mule.util.store.InMemoryObjectStore;
22
23 import java.text.MessageFormat;
24
25
26
27
28
29
30
31
32 public class IdempotentMessageFilter extends AbstractFilteringMessageProcessor implements FlowConstructAware, Initialisable
33 {
34 protected volatile ObjectStore<String> store;
35 protected volatile String assignedComponentName;
36 protected FlowConstruct flowConstruct;
37
38 protected String idExpression = MessageFormat.format("{0}message:id{1}",
39 ExpressionManager.DEFAULT_EXPRESSION_PREFIX, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX);
40
41 protected String valueExpression = MessageFormat.format("{0}message:id{1}",
42 ExpressionManager.DEFAULT_EXPRESSION_PREFIX, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX);
43
44 public IdempotentMessageFilter()
45 {
46 super();
47 }
48
49 public void initialise() throws InitialisationException
50 {
51 if (store == null)
52 {
53 this.store = this.createMessageIdStore();
54 }
55 }
56
57 protected ObjectStore<String> createMessageIdStore() throws InitialisationException
58 {
59 InMemoryObjectStore<String> s = new InMemoryObjectStore<String>();
60 s.setName(assignedComponentName);
61 s.setMaxEntries(-1);
62 s.setEntryTTL(60 * 5 * 1000);
63 s.setExpirationInterval(6000);
64 s.initialise();
65 return s;
66 }
67
68 @Override
69 protected MuleEvent processNext(MuleEvent event) throws MuleException
70 {
71 String id = this.getIdForEvent(event);
72 String value = this.getValueForEvent(event);
73 try
74 {
75 store.store(id, value);
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 getValueForEvent(MuleEvent event) throws MessagingException
86 {
87 return event.getMuleContext().getExpressionManager().parse(valueExpression, event.getMessage(), true);
88 }
89
90 protected String getIdForEvent(MuleEvent event) throws MessagingException
91 {
92 return event.getMuleContext().getExpressionManager().parse(idExpression, event.getMessage(), true);
93 }
94
95 public String getIdExpression()
96 {
97 return idExpression;
98 }
99
100 public void setIdExpression(String idExpression)
101 {
102 this.idExpression = idExpression;
103 }
104
105 public ObjectStore<String> getStore()
106 {
107 return store;
108 }
109
110 public void setStore(ObjectStore<String> store)
111 {
112 this.store = store;
113 }
114
115 @Override
116 protected boolean accept(MuleEvent event)
117 {
118 return event != null && acceptMessageForFlowConstruct(event) && isNewMessage(event);
119 }
120
121 protected boolean acceptMessageForFlowConstruct(MuleEvent event)
122 {
123 if (flowConstruct.getName().equals(event.getFlowConstruct().getName()))
124 {
125 return true;
126 }
127 else
128 {
129 logger.error("This IdempotentMessageFilter was configured on the service: "
130 + assignedComponentName + " but has received an event for service: "
131 + flowConstruct.getName() + ". Please check your config to make sure each service"
132 + "has its own instance of IdempotentMessageFilter.");
133 return false;
134 }
135 }
136
137 protected boolean isNewMessage(MuleEvent event)
138 {
139 try
140 {
141 String id = this.getIdForEvent(event);
142 if (store == null)
143 {
144 synchronized (this)
145 {
146 initialise();
147 }
148 }
149 return !store.contains(id);
150 }
151 catch (MuleException e)
152 {
153 logger.error("Exception attempting to determine idempotency of incoming message for "
154 + event.getFlowConstruct().getName() + " from the endpoint "
155 + event.getEndpoint().getEndpointURI().getUri(), e);
156 return false;
157 }
158 }
159
160 public void setFlowConstruct(FlowConstruct flowConstruct)
161 {
162 this.flowConstruct = flowConstruct;
163 }
164
165 public String getValueExpression()
166 {
167 return valueExpression;
168 }
169
170 public void setValueExpression(String valueExpression)
171 {
172 this.valueExpression = valueExpression;
173 }
174
175 }