1
2
3
4
5
6
7
8
9
10
11 package org.mule.util.store;
12
13 import org.mule.api.MuleContext;
14 import org.mule.api.MuleRuntimeException;
15 import org.mule.api.context.MuleContextAware;
16 import org.mule.api.store.ObjectAlreadyExistsException;
17 import org.mule.api.store.ObjectDoesNotExistException;
18 import org.mule.api.store.ObjectStoreException;
19 import org.mule.api.store.PartitionableExpirableObjectStore;
20 import org.mule.config.i18n.CoreMessages;
21 import org.mule.config.i18n.Message;
22 import org.mule.util.FileUtils;
23 import org.mule.util.SerializationUtils;
24
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.Serializable;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.List;
36 import java.util.concurrent.TimeUnit;
37
38 import org.apache.commons.lang.SerializationException;
39
40 public class PartitionedPersistentObjectStore<T extends Serializable> extends
41 AbstractPartitionedObjectStore<T> implements MuleContextAware, PartitionableExpirableObjectStore<T>
42 {
43 MuleContext muleContext;
44 public static final String DEFAULT_OBJECT_STORE = "objectstore";
45 private static final String FILE_EXTENSION = ".obj";
46 private File storeDirectory;
47
48 public PartitionedPersistentObjectStore()
49 {
50 super();
51 }
52
53 public PartitionedPersistentObjectStore(MuleContext context)
54 {
55 super();
56 muleContext = context;
57 }
58
59 @Override
60 public void open(String partitionName) throws ObjectStoreException
61 {
62 initStoreDirectory();
63 if (!storeDirectory.exists())
64 {
65 createStoreDirectory(storeDirectory);
66 }
67 }
68
69 @Override
70 public void close(String partitionName) throws ObjectStoreException
71 {
72
73 }
74
75 @Override
76 public boolean isPersistent()
77 {
78 return true;
79 }
80
81 @Override
82 public boolean contains(Serializable key, String partitionName) throws ObjectStoreException
83 {
84 File storeFile = createStoreFile(key, partitionName);
85 return storeFile.exists();
86 }
87
88 @Override
89 public void store(Serializable key, T value, String partitionName) throws ObjectStoreException
90 {
91 File outputFile = createStoreFile(key, partitionName);
92 ensureStoreDirectoryExists(outputFile);
93 boolean isNewFile;
94 try
95 {
96 isNewFile = outputFile.createNewFile();
97 }
98 catch (IOException e)
99 {
100 throw new ObjectStoreException(e);
101 }
102 if (!isNewFile)
103 {
104 throw new ObjectAlreadyExistsException();
105 }
106 serialize(value, outputFile);
107 }
108
109 @Override
110 public T retrieve(Serializable key, String partitionName) throws ObjectStoreException
111 {
112 File file = createStoreFile(key, partitionName);
113 return deserialize(file);
114 }
115
116 @Override
117 public T remove(Serializable key, String partitionName) throws ObjectStoreException
118 {
119 File storeFile = createStoreFile(key, partitionName);
120 T result = deserialize(storeFile);
121 deleteStoreFile(storeFile);
122
123 return result;
124 }
125
126 @Override
127 public List<Serializable> allKeys(String partitionName) throws ObjectStoreException
128 {
129 if (storeDirectory == null)
130 {
131 return Collections.emptyList();
132 }
133
134 return collectAllKeys(partitionName);
135 }
136
137 protected List<Serializable> collectAllKeys(String partitionName) throws ObjectStoreException
138 {
139 try
140 {
141 List<Serializable> keys = new ArrayList<Serializable>();
142 listStoredFiles(createStorePartition(partitionName), keys);
143
144 return keys;
145 }
146 catch (ClassNotFoundException e)
147 {
148 String message = String.format("Could not restore from %1s", storeDirectory.getAbsolutePath());
149 throw new ObjectStoreException(CoreMessages.createStaticMessage(message));
150 }
151 catch (IOException e)
152 {
153 String message = String.format("Could not restore from %1s", storeDirectory.getAbsolutePath());
154 throw new ObjectStoreException(CoreMessages.createStaticMessage(message));
155 }
156 }
157
158 protected void listStoredFiles(File directory, List<Serializable> keys)
159 throws IOException, ClassNotFoundException
160 {
161 File[] files = directory.listFiles();
162 if (files == null)
163 {
164 return;
165 }
166
167
168
169 Arrays.sort(files);
170
171 for (int i = 0; i < files.length; i++)
172 {
173 if (files[i].isDirectory())
174 {
175 listStoredFiles(files[i], keys);
176 }
177 else if (files[i].getName().endsWith(FILE_EXTENSION))
178 {
179 String id = files[i].getCanonicalPath();
180
181 int beginIndex = storeDirectory.getCanonicalPath().length() + 1;
182 int length = id.length() - FILE_EXTENSION.length();
183 id = id.substring(beginIndex, length);
184
185 String partition = id.substring(0, id.indexOf(File.separator));
186 id = id.substring(partition.length() + 1);
187
188 keys.add(id);
189 }
190 }
191 }
192
193 @Override
194 public List<String> allPartitions() throws ObjectStoreException
195 {
196 File[] files = storeDirectory.listFiles();
197 if (files == null)
198 {
199 return new ArrayList<String>();
200 }
201
202
203
204 Arrays.sort(files);
205 List<String> partitions = new ArrayList<String>();
206
207 for (int i = 0; i < files.length; i++)
208 {
209 if (files[i].isDirectory())
210 {
211 partitions.add(files[i].getName());
212 }
213 }
214 return partitions;
215 }
216
217 private void initStoreDirectory() throws ObjectStoreException
218 {
219 try
220 {
221 String workingDirectory = muleContext.getConfiguration().getWorkingDirectory();
222 String path = workingDirectory + File.separator + DEFAULT_OBJECT_STORE;
223 storeDirectory = FileUtils.newFile(path);
224 }
225 catch (MuleRuntimeException mre)
226 {
227
228
229
230
231
232 throw new ObjectStoreException(mre);
233 }
234 }
235
236 protected synchronized void createStoreDirectory(File directory) throws ObjectStoreException
237 {
238
239
240 if (!directory.exists() && !directory.mkdirs())
241 {
242 Message message = CoreMessages.failedToCreate("object store directory "
243 + directory.getAbsolutePath());
244 throw new ObjectStoreException(message);
245 }
246 }
247
248 protected File createStorePartition(String partitionName) throws ObjectStoreException
249 {
250 try
251 {
252 return FileUtils.newFile(storeDirectory, partitionName);
253 }
254 catch (MuleRuntimeException mre)
255 {
256
257
258
259
260
261 throw new ObjectStoreException(mre);
262 }
263 }
264
265 protected File createStoreFile(Serializable key, String partitionName) throws ObjectStoreException
266 {
267 String filename = key + FILE_EXTENSION;
268 String path = partitionName + File.separator + filename;
269
270 try
271 {
272 return FileUtils.newFile(storeDirectory, path);
273 }
274 catch (MuleRuntimeException mre)
275 {
276
277
278
279
280
281 throw new ObjectStoreException(mre);
282 }
283 }
284
285 protected void ensureStoreDirectoryExists(File outputFile) throws ObjectStoreException
286 {
287 File directory = outputFile.getParentFile();
288 if (!directory.exists())
289 {
290 createStoreDirectory(directory);
291 }
292 }
293
294 protected void serialize(T value, File outputFile) throws ObjectStoreException
295 {
296 try
297 {
298 FileOutputStream out = new FileOutputStream(outputFile);
299 SerializationUtils.serialize(value, out);
300 }
301 catch (SerializationException se)
302 {
303 throw new ObjectStoreException(se);
304 }
305 catch (FileNotFoundException fnfe)
306 {
307 throw new ObjectStoreException(fnfe);
308 }
309 }
310
311 @SuppressWarnings("unchecked")
312 protected T deserialize(File file) throws ObjectStoreException
313 {
314 try
315 {
316 FileInputStream in = new FileInputStream(file);
317 return (T) SerializationUtils.deserialize(in, muleContext);
318 }
319 catch (SerializationException se)
320 {
321 throw new ObjectStoreException(se);
322 }
323 catch (FileNotFoundException fnfe)
324 {
325 throw new ObjectDoesNotExistException(fnfe);
326 }
327 }
328
329 protected void deleteStoreFile(File file) throws ObjectStoreException
330 {
331 if (file.exists())
332 {
333 if (!file.delete())
334 {
335 Message message = CoreMessages.createStaticMessage("Deleting " + file.getAbsolutePath()
336 + " failed");
337 throw new ObjectStoreException(message);
338 }
339 }
340 else
341 {
342 throw new ObjectDoesNotExistException();
343 }
344 }
345
346 public void open() throws ObjectStoreException
347 {
348 initStoreDirectory();
349 if (!storeDirectory.exists())
350 {
351 createStoreDirectory(storeDirectory);
352 }
353 }
354
355 @Override
356 public void setMuleContext(MuleContext context)
357 {
358 muleContext = context;
359 }
360
361 @Override
362 public void expire(int entryTTL, int maxEntries) throws ObjectStoreException
363 {
364 expire(entryTTL, maxEntries, DEFAULT_PARTITION);
365 }
366
367 @Override
368 public void expire(int entryTTL, int maxEntries, String partitionName) throws ObjectStoreException
369 {
370 File partitionFolder = FileUtils.newFile(storeDirectory, partitionName);
371 File[] files = partitionFolder.listFiles();
372 if (files == null)
373 {
374 return;
375 }
376 Arrays.sort(files, new Comparator<File>()
377 {
378 public int compare(File f1, File f2)
379 {
380 int result=Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
381 if(result==0)
382 {
383 result=f1.getName().compareTo(f2.getName());
384 }
385 return result;
386 }
387 });
388 int startIndex = trimToMaxSize(files, maxEntries);
389
390 final long now = System.currentTimeMillis();
391
392 for (int i = startIndex; i < files.length; i++)
393 {
394 if (files[i].getName().endsWith(FILE_EXTENSION))
395 {
396 Long lastModified = files[i].lastModified();
397 if ((now - lastModified) >= entryTTL)
398 {
399 deleteStoreFile(files[i]);
400 }
401 else
402 {
403 break;
404 }
405 }
406 }
407
408 }
409
410 private int trimToMaxSize(File[] files, int maxEntries) throws ObjectStoreException
411 {
412 if (maxEntries < 0)
413 {
414 return 0;
415 }
416 int expired = 0;
417 int excess = (files.length - maxEntries);
418 if (excess > 0)
419 {
420 for (int i = 0; i < excess; i++)
421 {
422 deleteStoreFile(files[i]);
423 expired++;
424 }
425 }
426 return expired;
427 }
428
429 @Override
430 public void disposePartition(String partitionName) throws ObjectStoreException
431 {
432 File partitionFolder = FileUtils.newFile(storeDirectory, partitionName);
433 FileUtils.deleteQuietly(partitionFolder);
434 }
435
436 }