1
2
3
4
5
6
7
8
9
10
11 package org.mule.util.store;
12
13 import org.mule.api.store.ObjectAlreadyExistsException;
14 import org.mule.api.store.ObjectDoesNotExistException;
15 import org.mule.api.store.ObjectStoreException;
16 import org.mule.api.store.PartitionableExpirableObjectStore;
17
18 import java.io.Serializable;
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.ConcurrentSkipListMap;
27 import java.util.concurrent.TimeUnit;
28
29 public class PartitionedInMemoryObjectStore<T extends Serializable> extends AbstractPartitionedObjectStore<T>
30 implements PartitionableExpirableObjectStore<T>
31 {
32 private ConcurrentMap<String, ConcurrentMap<Serializable, T>> partitions = new ConcurrentHashMap<String, ConcurrentMap<Serializable, T>>();
33 private ConcurrentMap<String, ConcurrentSkipListMap<Long, Serializable>> expiryInfoPartition = new ConcurrentHashMap<String, ConcurrentSkipListMap<Long, Serializable>>();
34
35 @Override
36 public boolean isPersistent()
37 {
38 return false;
39 }
40
41 @Override
42 public boolean contains(Serializable key, String partitionName) throws ObjectStoreException
43 {
44 if (partitions.containsKey(partitionName))
45 {
46 return partitions.get(partitionName).containsKey(key);
47 }
48 else
49 {
50 return false;
51 }
52 }
53
54 @Override
55 public void store(Serializable key, T value, String partitionName) throws ObjectStoreException
56 {
57 T oldValue = getPartition(partitionName).putIfAbsent(key, value);
58 if (oldValue != null)
59 {
60 throw new ObjectAlreadyExistsException();
61 }
62 getExpirtyInfoPartition(partitionName).put(Long.valueOf(System.nanoTime()), key);
63 }
64
65 @Override
66 public T retrieve(Serializable key, String partitionName) throws ObjectStoreException
67 {
68 T value = getPartition(partitionName).get(key);
69 if (value == null)
70 {
71 throw new ObjectDoesNotExistException();
72 }
73 return value;
74 }
75
76 @Override
77 public T remove(Serializable key, String partitionName) throws ObjectStoreException
78 {
79 T removedValue = getPartition(partitionName).remove(key);
80 if (removedValue == null)
81 {
82 throw new ObjectDoesNotExistException();
83 }
84
85
86 Iterator<Map.Entry<Long, Serializable>> localIterator = getExpirtyInfoPartition(partitionName).entrySet()
87 .iterator();
88 Map.Entry<Long, Serializable> localEntry;
89 Long timestamp = null;
90 while (localIterator.hasNext())
91 {
92 localEntry = localIterator.next();
93 if (key.equals(localEntry.getValue()))
94 {
95 timestamp = localEntry.getKey();
96 break;
97 }
98 }
99 getExpirtyInfoPartition(partitionName).remove(timestamp);
100 return removedValue;
101 }
102
103 @Override
104 public List<Serializable> allKeys(String partitionName) throws ObjectStoreException
105 {
106 return new ArrayList<Serializable>(getPartition(partitionName).keySet());
107 }
108
109 @Override
110 public List<String> allPartitions() throws ObjectStoreException
111 {
112 return new ArrayList<String>(partitions.keySet());
113 }
114
115 private ConcurrentMap<Serializable, T> getPartition(String partitionName)
116 {
117 ConcurrentMap<Serializable, T> partition = partitions.get(partitionName);
118 if (partition == null)
119 {
120 partition = new ConcurrentHashMap<Serializable, T>();
121 ConcurrentMap<Serializable, T> previous = partitions.putIfAbsent(partitionName, partition);
122 if (previous != null)
123 {
124 partition = previous;
125 }
126 }
127 return partition;
128 }
129
130 private ConcurrentSkipListMap<Long, Serializable> getExpirtyInfoPartition(String partitionName)
131 {
132 ConcurrentSkipListMap<Long, Serializable> partition = expiryInfoPartition.get(partitionName);
133 if (partition == null)
134 {
135 partition = new ConcurrentSkipListMap<Long, Serializable>();
136 ConcurrentSkipListMap<Long, Serializable> previous = expiryInfoPartition.putIfAbsent(
137 partitionName, partition);
138 if (previous != null)
139 {
140 partition = previous;
141 }
142 }
143 return partition;
144 }
145
146 @Override
147 public void open(String partitionName) throws ObjectStoreException
148 {
149
150 }
151
152 @Override
153 public void close(String partitionName) throws ObjectStoreException
154 {
155
156 }
157
158 @Override
159 public void expire(int entryTTL, int maxEntries) throws ObjectStoreException
160 {
161 expire(entryTTL, maxEntries, DEFAULT_PARTITION);
162 }
163
164 @Override
165 public void expire(int entryTTL, int maxEntries, String partitionName) throws ObjectStoreException
166 {
167 final long now = System.nanoTime();
168 int expiredEntries = 0;
169 Map.Entry<Long, Serializable> oldestEntry;
170 ConcurrentSkipListMap<Long, Serializable> store = getExpirtyInfoPartition(partitionName);
171 ConcurrentMap<Serializable, T> partition = getPartition(partitionName);
172
173 trimToMaxSize(store, maxEntries, partition);
174
175 while ((oldestEntry = store.firstEntry()) != null)
176 {
177 Long oldestKey = (Long) oldestEntry.getKey();
178 long oldestKeyValue = oldestKey.longValue();
179
180 if (TimeUnit.NANOSECONDS.toMillis(now - oldestKeyValue) >= entryTTL)
181 {
182 partition.remove(oldestEntry.getValue());
183 store.remove(oldestKey);
184 expiredEntries++;
185 }
186 else
187 {
188 break;
189 }
190 }
191
192 if (logger.isDebugEnabled())
193 {
194 logger.debug("Expired " + expiredEntries + " old entries");
195 }
196 }
197
198 private void trimToMaxSize(ConcurrentSkipListMap<Long, Serializable> store,
199 int maxEntries,
200 ConcurrentMap<Serializable, T> partition)
201 {
202 if (maxEntries < 0)
203 {
204 return;
205 }
206 int currentSize = store.size();
207 int excess = (currentSize - maxEntries);
208 if (excess > 0)
209 {
210 while (currentSize > maxEntries)
211 {
212 Entry<Long, Serializable> toRemove = store.pollFirstEntry();
213 partition.remove(toRemove.getValue());
214 currentSize--;
215 }
216
217 if (logger.isDebugEnabled())
218 {
219 logger.debug("Expired " + excess + " excess entries");
220 }
221 }
222 }
223
224 @Override
225 public void disposePartition(String partitionName) throws ObjectStoreException
226 {
227 partitions.remove(partitionName);
228 expiryInfoPartition.remove(partitionName);
229 }
230
231 }