1
2
3
4
5
6
7
8
9
10
11 package org.mule.util.store;
12
13 import org.mule.api.DefaultMuleException;
14 import org.mule.api.MuleContext;
15 import org.mule.api.MuleException;
16 import org.mule.api.config.MuleProperties;
17 import org.mule.api.context.MuleContextAware;
18 import org.mule.api.lifecycle.Disposable;
19 import org.mule.api.lifecycle.Initialisable;
20 import org.mule.api.lifecycle.InitialisationException;
21 import org.mule.api.store.ListableObjectStore;
22 import org.mule.api.store.ObjectStoreException;
23 import org.mule.config.i18n.CoreMessages;
24 import org.mule.util.UUID;
25 import org.mule.util.concurrent.DaemonThreadFactory;
26
27 import java.io.Serializable;
28 import java.util.Comparator;
29 import java.util.List;
30 import java.util.PriorityQueue;
31 import java.util.concurrent.ScheduledThreadPoolExecutor;
32 import java.util.concurrent.TimeUnit;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37
38
39
40
41 public class MonitoredObjectStoreWrapper<T extends Serializable>
42 implements ListableObjectStore<T>, Runnable, MuleContextAware, Initialisable, Disposable
43 {
44 protected MuleContext context;
45 protected ScheduledThreadPoolExecutor scheduler;
46 ListableObjectStore<StoredObject<T>> baseStore;
47 private static Log logger = LogFactory.getLog(MonitoredObjectStoreWrapper.class);
48
49
50
51
52
53 protected int maxEntries = 4000;
54
55
56
57
58
59
60 protected int entryTTL = -1;
61
62
63
64
65
66
67
68 protected int expirationInterval = 1000;
69
70
71
72
73 protected String name = null;
74
75 public MonitoredObjectStoreWrapper(ListableObjectStore<StoredObject<T>> baseStore)
76 {
77 this.baseStore = baseStore;
78 }
79
80 public MonitoredObjectStoreWrapper(ListableObjectStore<StoredObject<T>> baseStore,
81 int maxEntries,
82 int entryTTL,
83 int expirationInterval)
84 {
85 this.baseStore = baseStore;
86 this.maxEntries = maxEntries;
87 this.entryTTL = entryTTL;
88 this.expirationInterval = expirationInterval;
89 }
90
91 @Override
92 public boolean contains(Serializable key) throws ObjectStoreException
93 {
94 return getStore().contains(key);
95 }
96
97 @Override
98 public void store(Serializable key, T value) throws ObjectStoreException
99 {
100 Long time = Long.valueOf(System.nanoTime());
101 getStore().store(key, new StoredObject<T>(value, time, key));
102 }
103
104 @Override
105 public T retrieve(Serializable key) throws ObjectStoreException
106 {
107 return getStore().retrieve(key).getItem();
108 }
109
110 @Override
111 public T remove(Serializable key) throws ObjectStoreException
112 {
113 StoredObject<T> object = getStore().remove(key);
114 if (object == null)
115 {
116 return null;
117 }
118 else
119 {
120 return object.getItem();
121 }
122 }
123
124 @Override
125 public boolean isPersistent()
126 {
127 return getStore().isPersistent();
128 }
129
130 @Override
131 public void open() throws ObjectStoreException
132 {
133 getStore().open();
134 }
135
136 @Override
137 public void close() throws ObjectStoreException
138 {
139 getStore().close();
140 }
141
142 @Override
143 public List<Serializable> allKeys() throws ObjectStoreException
144 {
145 return getStore().allKeys();
146 }
147
148 private ListableObjectStore<StoredObject<T>> getStore()
149 {
150 if (baseStore == null)
151 {
152 baseStore = context.getRegistry().lookupObject(
153 MuleProperties.OBJECT_STORE_DEFAULT_PERSISTENT_NAME);
154 }
155 return baseStore;
156 }
157
158 @Override
159 public void setMuleContext(MuleContext context)
160 {
161 this.context = context;
162 }
163
164 @Override
165 public void run()
166 {
167 if (context.isPrimaryPollingInstance())
168 {
169 expire();
170 }
171 }
172
173 public void expire()
174 {
175 try
176 {
177 final long now = System.nanoTime();
178 List<Serializable> keys = allKeys();
179 int excess = (allKeys().size() - maxEntries);
180 if (maxEntries > 0 && excess > 0)
181 {
182 PriorityQueue<StoredObject<T>> q = new PriorityQueue<StoredObject<T>>(excess,
183 new Comparator<StoredObject<T>>()
184 {
185
186 @Override
187 public int compare(StoredObject<T> paramT1, StoredObject<T> paramT2)
188 {
189 return paramT2.timestamp.compareTo(paramT1.timestamp);
190 }
191 });
192 long youngest = Long.MAX_VALUE;
193 for (Serializable key : keys)
194 {
195 StoredObject<T> obj = getStore().retrieve(key);
196
197 if (entryTTL>0 && TimeUnit.NANOSECONDS.toMillis(now - obj.getTimestamp()) >= entryTTL)
198 {
199 remove(key);
200 excess--;
201 if (excess > 0 && q.size() > excess)
202 {
203 q.poll();
204 youngest = q.peek().timestamp;
205 }
206 }
207 else
208 {
209 if (excess > 0 && (q.size() < excess || obj.timestamp < youngest))
210 {
211 q.offer(obj);
212 youngest = q.peek().timestamp;
213 }
214 if (excess > 0 && q.size() > excess)
215 {
216 q.poll();
217 youngest = q.peek().timestamp;
218 }
219
220 }
221 }
222 for (int i = 0; i < excess; i++)
223 {
224 Serializable key = q.poll().key;
225 remove(key);
226 }
227 }
228 else
229 {
230 if(entryTTL>0)
231 {
232 for (Serializable key : keys)
233 {
234 StoredObject<T> obj = getStore().retrieve(key);
235 if (TimeUnit.NANOSECONDS.toMillis(now - obj.getTimestamp()) >= entryTTL)
236 {
237 remove(key);
238 }
239 }
240 }
241 }
242 }
243 catch (Exception e)
244 {
245 logger.warn("Running expirty on " + baseStore + " threw " + e + ":" + e.getMessage());
246 }
247 }
248
249 @Override
250 public void dispose()
251 {
252 if (scheduler != null)
253 {
254 scheduler.shutdown();
255 }
256 }
257
258 @Override
259 public void initialise() throws InitialisationException
260 {
261 if (name == null)
262 {
263 name = UUID.getUUID();
264 }
265
266 if (expirationInterval <= 0)
267 {
268 throw new IllegalArgumentException(CoreMessages.propertyHasInvalidValue("expirationInterval",
269 new Integer(expirationInterval)).toString());
270 }
271
272 if (scheduler == null)
273 {
274 this.scheduler = new ScheduledThreadPoolExecutor(1);
275 scheduler.setThreadFactory(new DaemonThreadFactory(name + "-Monitor", this.getClass()
276 .getClassLoader()));
277 scheduler.scheduleWithFixedDelay(this, 0, expirationInterval, TimeUnit.MILLISECONDS);
278 }
279 }
280
281 protected static class StoredObject<T> implements Serializable, DeserializationPostInitialisable
282 {
283 private static final long serialVersionUID = 8656763235928199259L;
284 final private T item;
285 final private Long timestamp;
286 final private Serializable key;
287
288 public StoredObject(T item, Long timestamp, Serializable key)
289 {
290 super();
291 this.item = item;
292 this.timestamp = timestamp;
293 this.key = key;
294 }
295
296 public T getItem()
297 {
298 return item;
299 }
300
301 public Long getTimestamp()
302 {
303 return timestamp;
304 }
305
306 public Serializable getKey()
307 {
308 return key;
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322 @SuppressWarnings({"unused", "unchecked"})
323 private void initAfterDeserialisation(MuleContext muleContext) throws MuleException
324 {
325 if (item instanceof DeserializationPostInitialisable)
326 {
327 try
328 {
329 DeserializationPostInitialisable.Implementation.init(item, muleContext);
330 }
331 catch (Exception e)
332 {
333 throw new DefaultMuleException(e);
334 }
335 }
336 }
337 }
338
339 }