1 |
| package org.jboss.cache.loader; |
2 |
| |
3 |
| import net.jcip.annotations.ThreadSafe; |
4 |
| import org.apache.commons.logging.Log; |
5 |
| import org.apache.commons.logging.LogFactory; |
6 |
| import org.jboss.cache.Fqn; |
7 |
| import org.jboss.cache.Modification; |
8 |
| import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; |
9 |
| import org.jboss.cache.lock.StripedLock; |
10 |
| import org.jboss.cache.marshall.ObjectSerializationFactory; |
11 |
| |
12 |
| import java.io.File; |
13 |
| import java.io.FileInputStream; |
14 |
| import java.io.FileNotFoundException; |
15 |
| import java.io.FileOutputStream; |
16 |
| import java.io.IOException; |
17 |
| import java.io.ObjectInputStream; |
18 |
| import java.io.ObjectOutputStream; |
19 |
| import java.util.Collections; |
20 |
| import java.util.HashMap; |
21 |
| import java.util.HashSet; |
22 |
| import java.util.List; |
23 |
| import java.util.Map; |
24 |
| import java.util.Set; |
25 |
| import java.util.concurrent.ConcurrentHashMap; |
26 |
| import java.util.regex.Matcher; |
27 |
| import java.util.regex.Pattern; |
28 |
| |
29 |
| |
30 |
| |
31 |
| |
32 |
| |
33 |
| |
34 |
| |
35 |
| |
36 |
| |
37 |
| |
38 |
| |
39 |
| |
40 |
| |
41 |
| |
42 |
| |
43 |
| |
44 |
| |
45 |
| |
46 |
| |
47 |
| |
48 |
| |
49 |
| |
50 |
| |
51 |
| |
52 |
| |
53 |
| @ThreadSafe |
54 |
| public class FileCacheLoader extends AbstractCacheLoader |
55 |
| { |
56 |
| File root = null; |
57 |
| String rootPath = null; |
58 |
| Log log = LogFactory.getLog(getClass()); |
59 |
| |
60 |
| protected final StripedLock lock = new StripedLock(); |
61 |
| |
62 |
| private FileCacheLoaderConfig config; |
63 |
| |
64 |
| |
65 |
| |
66 |
| |
67 |
| |
68 |
| Map<Object, List<Modification>> transactions = new ConcurrentHashMap<Object, List<Modification>>(); |
69 |
| |
70 |
| |
71 |
| |
72 |
| |
73 |
| public static final String DATA = "data.dat"; |
74 |
| |
75 |
| |
76 |
| |
77 |
| |
78 |
| public static final String DIR_SUFFIX = "fdb"; |
79 |
| |
80 |
| |
81 |
| |
82 |
| |
83 |
| public static final Pattern PATH_PATTERN = Pattern.compile("[\\*<>|\"?]"); |
84 |
| |
85 |
| |
86 |
| |
87 |
| |
88 |
| public static final Pattern FQN_PATTERN = Pattern.compile("[\\\\\\/:*<>|\"?]"); |
89 |
| |
90 |
364
| public void setConfig(IndividualCacheLoaderConfig base)
|
91 |
| { |
92 |
364
| if (base instanceof FileCacheLoaderConfig)
|
93 |
| { |
94 |
1
| this.config = (FileCacheLoaderConfig) base;
|
95 |
| } |
96 |
363
| else if (base != null)
|
97 |
| { |
98 |
363
| this.config = new FileCacheLoaderConfig(base);
|
99 |
| } |
100 |
| |
101 |
364
| String location = this.config != null ? this.config.getLocation() : null;
|
102 |
364
| if (location != null && location.length() > 0)
|
103 |
| { |
104 |
313
| root = new File(location);
|
105 |
313
| rootPath = root.getAbsolutePath() + File.separator;
|
106 |
| } |
107 |
| } |
108 |
| |
109 |
364
| public IndividualCacheLoaderConfig getConfig()
|
110 |
| { |
111 |
364
| return config;
|
112 |
| } |
113 |
| |
114 |
334
| public void create() throws Exception
|
115 |
| { |
116 |
334
| lock.acquireLock(Fqn.ROOT, true);
|
117 |
334
| try
|
118 |
| { |
119 |
334
| if (root == null)
|
120 |
| { |
121 |
52
| String tmpLocation = System.getProperty("java.io.tmpdir", "C:\\tmp");
|
122 |
52
| root = new File(tmpLocation);
|
123 |
52
| rootPath = root.getAbsolutePath() + File.separator;
|
124 |
| } |
125 |
334
| if (!root.exists())
|
126 |
| { |
127 |
204
| if (log.isTraceEnabled())
|
128 |
| { |
129 |
0
| log.trace("Creating cache loader location " + root);
|
130 |
| } |
131 |
| |
132 |
204
| if (config.isCheckCharacterPortability())
|
133 |
| { |
134 |
| |
135 |
| |
136 |
204
| isCharacterPortableLocation(root.getAbsolutePath());
|
137 |
| } |
138 |
| |
139 |
204
| boolean created = root.mkdirs();
|
140 |
204
| if (!created)
|
141 |
| { |
142 |
0
| throw new IOException("Unable to create cache loader location " + root);
|
143 |
| } |
144 |
| } |
145 |
| |
146 |
334
| if (!root.isDirectory())
|
147 |
| { |
148 |
0
| throw new IOException("Cache loader location [" + root + "] is not a directory!");
|
149 |
| } |
150 |
| } |
151 |
| finally |
152 |
| { |
153 |
334
| lock.releaseLock(Fqn.ROOT);
|
154 |
| } |
155 |
| } |
156 |
| |
157 |
334
| public void start() throws Exception
|
158 |
| { |
159 |
| } |
160 |
| |
161 |
334
| public void stop()
|
162 |
| { |
163 |
| } |
164 |
| |
165 |
333
| public void destroy()
|
166 |
| { |
167 |
| } |
168 |
| |
169 |
6467
| public Set<String> getChildrenNames(Fqn fqn) throws Exception
|
170 |
| { |
171 |
6467
| lock.acquireLock(fqn, true);
|
172 |
6467
| try
|
173 |
| { |
174 |
6467
| File parent = getDirectory(fqn, false);
|
175 |
2623
| if (parent == null) return null;
|
176 |
3844
| File[] children = parent.listFiles();
|
177 |
3844
| Set<String> s = new HashSet<String>();
|
178 |
3844
| for (File child : children)
|
179 |
| { |
180 |
4900
| if (child.isDirectory() && child.getName().endsWith(DIR_SUFFIX))
|
181 |
| { |
182 |
346
| String child_name = child.getName();
|
183 |
346
| child_name = child_name.substring(0, child_name.lastIndexOf(DIR_SUFFIX) - 1);
|
184 |
346
| s.add(child_name);
|
185 |
| } |
186 |
| } |
187 |
3844
| return s.size() == 0 ? null : s;
|
188 |
| } |
189 |
| finally |
190 |
| { |
191 |
6467
| lock.releaseLock(fqn);
|
192 |
| } |
193 |
| } |
194 |
| |
195 |
5872
| public Map get(Fqn fqn) throws Exception
|
196 |
| { |
197 |
5872
| lock.acquireLock(fqn, true);
|
198 |
5872
| try
|
199 |
| { |
200 |
5872
| return loadAttributes(fqn);
|
201 |
| } |
202 |
| finally |
203 |
| { |
204 |
5872
| lock.releaseLock(fqn);
|
205 |
| } |
206 |
| } |
207 |
| |
208 |
1920
| public boolean exists(Fqn fqn) throws Exception
|
209 |
| { |
210 |
1920
| lock.acquireLock(fqn, true);
|
211 |
1920
| try
|
212 |
| { |
213 |
1920
| File f = getDirectory(fqn, false);
|
214 |
1920
| return f != null;
|
215 |
| } |
216 |
| finally |
217 |
| { |
218 |
1920
| lock.releaseLock(fqn);
|
219 |
| } |
220 |
| } |
221 |
| |
222 |
7845
| public Object put(Fqn fqn, Object key, Object value) throws Exception
|
223 |
| { |
224 |
7845
| lock.acquireLock(fqn, true);
|
225 |
7845
| try
|
226 |
| { |
227 |
7845
| Object retval;
|
228 |
7845
| Map m = loadAttributes(fqn);
|
229 |
511
| if (m == null) m = new HashMap();
|
230 |
7845
| retval = m.put(key, value);
|
231 |
7845
| storeAttributes(fqn, m);
|
232 |
7845
| return retval;
|
233 |
| } |
234 |
| finally |
235 |
| { |
236 |
7845
| lock.releaseLock(fqn);
|
237 |
| } |
238 |
| } |
239 |
| |
240 |
6563
| public void put(Fqn fqn, Map attributes) throws Exception
|
241 |
| { |
242 |
6563
| put(fqn, attributes, false);
|
243 |
| } |
244 |
| |
245 |
| |
246 |
6649
| public void put(Fqn fqn, Map attributes, boolean erase) throws Exception
|
247 |
| { |
248 |
6649
| lock.acquireLock(fqn, true);
|
249 |
6649
| try
|
250 |
| { |
251 |
6649
| Map m = erase ? new HashMap() : loadAttributes(fqn);
|
252 |
632
| if (m == null) m = new HashMap();
|
253 |
6649
| if (attributes != null)
|
254 |
| { |
255 |
6373
| m.putAll(attributes);
|
256 |
| } |
257 |
6649
| storeAttributes(fqn, m);
|
258 |
| } |
259 |
| finally |
260 |
| { |
261 |
6649
| lock.releaseLock(fqn);
|
262 |
| } |
263 |
| } |
264 |
| |
265 |
0
| void put(Fqn fqn) throws Exception
|
266 |
| { |
267 |
0
| getDirectory(fqn, true);
|
268 |
| } |
269 |
| |
270 |
6099
| public Object remove(Fqn fqn, Object key) throws Exception
|
271 |
| { |
272 |
6099
| lock.acquireLock(fqn, true);
|
273 |
6099
| try
|
274 |
| { |
275 |
6099
| Object retval;
|
276 |
6099
| Map m = loadAttributes(fqn);
|
277 |
3445
| if (m == null) return null;
|
278 |
2654
| retval = m.remove(key);
|
279 |
2654
| storeAttributes(fqn, m);
|
280 |
2654
| return retval;
|
281 |
| } |
282 |
| finally |
283 |
| { |
284 |
6099
| lock.releaseLock(fqn);
|
285 |
| } |
286 |
| } |
287 |
| |
288 |
6880
| public void remove(Fqn fqn) throws Exception
|
289 |
| { |
290 |
6880
| lock.acquireLock(fqn, true);
|
291 |
6880
| try
|
292 |
| { |
293 |
6880
| File dir = getDirectory(fqn, false);
|
294 |
6880
| if (dir != null)
|
295 |
| { |
296 |
1018
| boolean flag = removeDirectory(dir, true);
|
297 |
1018
| if (!flag)
|
298 |
| { |
299 |
0
| log.warn("failed removing " + fqn);
|
300 |
| } |
301 |
| } |
302 |
| } |
303 |
| finally |
304 |
| { |
305 |
6880
| lock.releaseLock(fqn);
|
306 |
| } |
307 |
| } |
308 |
| |
309 |
47
| public void removeData(Fqn fqn) throws Exception
|
310 |
| { |
311 |
47
| lock.acquireLock(fqn, true);
|
312 |
47
| try
|
313 |
| { |
314 |
47
| File f = getDirectory(fqn, false);
|
315 |
47
| if (f != null)
|
316 |
| { |
317 |
18
| File data = new File(f, DATA);
|
318 |
18
| if (data.exists())
|
319 |
| { |
320 |
18
| boolean flag = data.delete();
|
321 |
18
| if (!flag)
|
322 |
| { |
323 |
0
| log.warn("failed removing file " + data.getName());
|
324 |
| } |
325 |
| } |
326 |
| } |
327 |
| } |
328 |
| finally |
329 |
| { |
330 |
47
| lock.releaseLock(fqn);
|
331 |
| } |
332 |
| } |
333 |
| |
334 |
39
| public void prepare(Object tx, List<Modification> modifications, boolean one_phase) throws Exception
|
335 |
| { |
336 |
39
| if (one_phase)
|
337 |
| { |
338 |
14
| put(modifications);
|
339 |
| } |
340 |
| else |
341 |
| { |
342 |
25
| transactions.put(tx, modifications);
|
343 |
| } |
344 |
| } |
345 |
| |
346 |
17
| public void commit(Object tx) throws Exception
|
347 |
| { |
348 |
17
| List modifications = transactions.remove(tx);
|
349 |
17
| if (modifications == null)
|
350 |
| { |
351 |
0
| throw new Exception("transaction " + tx + " not found in transaction table");
|
352 |
| } |
353 |
17
| put(modifications);
|
354 |
| } |
355 |
| |
356 |
8
| public void rollback(Object tx)
|
357 |
| { |
358 |
8
| transactions.remove(tx);
|
359 |
| } |
360 |
| |
361 |
| |
362 |
| |
363 |
58841
| File getDirectory(Fqn fqn, boolean create)
|
364 |
| { |
365 |
58841
| File f = new File(getFullPath(fqn));
|
366 |
58841
| if (!f.exists())
|
367 |
| { |
368 |
17005
| if (create)
|
369 |
| { |
370 |
1229
| f.mkdirs();
|
371 |
| } |
372 |
| else |
373 |
| { |
374 |
15776
| return null;
|
375 |
| } |
376 |
| } |
377 |
43065
| return f;
|
378 |
| } |
379 |
| |
380 |
| |
381 |
| |
382 |
| |
383 |
| |
384 |
| |
385 |
| |
386 |
| |
387 |
| |
388 |
| |
389 |
1727
| boolean removeDirectory(File dir, boolean include_start_dir)
|
390 |
| { |
391 |
1727
| boolean success = true;
|
392 |
1727
| File[] subdirs = dir.listFiles();
|
393 |
1727
| for (File file : subdirs)
|
394 |
| { |
395 |
5458
| if (file.isFile() && file.getName().equals(DATA))
|
396 |
| { |
397 |
1036
| if (!file.delete())
|
398 |
| { |
399 |
0
| success = false;
|
400 |
| } |
401 |
1036
| continue;
|
402 |
| } |
403 |
4422
| if (file.isDirectory() && file.getName().endsWith(DIR_SUFFIX))
|
404 |
| { |
405 |
709
| if (!removeDirectory(file, false))
|
406 |
| { |
407 |
0
| success = false;
|
408 |
| } |
409 |
709
| if (!file.delete())
|
410 |
| { |
411 |
0
| success = false;
|
412 |
| } |
413 |
| } |
414 |
| } |
415 |
| |
416 |
1727
| if (include_start_dir)
|
417 |
| { |
418 |
1018
| if (!dir.equals(root))
|
419 |
| { |
420 |
738
| if (dir.delete())
|
421 |
| { |
422 |
738
| return success;
|
423 |
| } |
424 |
0
| success = false;
|
425 |
| } |
426 |
| } |
427 |
| |
428 |
989
| return success;
|
429 |
| } |
430 |
| |
431 |
58841
| String getFullPath(Fqn fqn)
|
432 |
| { |
433 |
58841
| StringBuffer sb = new StringBuffer(rootPath);
|
434 |
58841
| for (int i = 0; i < fqn.size(); i++)
|
435 |
| { |
436 |
207217
| Object tmp = fqn.get(i);
|
437 |
| |
438 |
207217
| String tmp_dir = tmp.toString();
|
439 |
207217
| sb.append(tmp_dir).append(".").append(DIR_SUFFIX).append(File.separator);
|
440 |
| } |
441 |
58841
| return sb.toString();
|
442 |
| } |
443 |
| |
444 |
26379
| protected Map loadAttributes(Fqn fqn) throws Exception
|
445 |
| { |
446 |
26379
| File f = getDirectory(fqn, false);
|
447 |
5676
| if (f == null) return null;
|
448 |
| |
449 |
20703
| File child = new File(f, DATA);
|
450 |
189
| if (!child.exists()) return new HashMap(0);
|
451 |
| |
452 |
| |
453 |
20514
| Map m = null;
|
454 |
20514
| try
|
455 |
| { |
456 |
20514
| m = (Map) unmarshall(child);
|
457 |
| } |
458 |
| catch (FileNotFoundException fnfe) |
459 |
| { |
460 |
| |
461 |
0
| m = Collections.emptyMap();
|
462 |
| } |
463 |
20514
| return m;
|
464 |
| } |
465 |
| |
466 |
17148
| protected void storeAttributes(Fqn fqn, Map attrs) throws Exception
|
467 |
| { |
468 |
17148
| File f = getDirectory(fqn, true);
|
469 |
17148
| File child = new File(f, DATA);
|
470 |
17148
| if (!child.exists())
|
471 |
| { |
472 |
1276
| if (config.isCheckCharacterPortability())
|
473 |
| { |
474 |
| |
475 |
1276
| isLengthPortablePath(child.getAbsolutePath());
|
476 |
| |
477 |
1276
| isCharacterPortableTree(fqn);
|
478 |
| } |
479 |
| |
480 |
1276
| if (!child.createNewFile())
|
481 |
| { |
482 |
0
| throw new IOException("Unable to create file: " + child);
|
483 |
| } |
484 |
| } |
485 |
| |
486 |
17148
| marshall(attrs, child);
|
487 |
| } |
488 |
| |
489 |
211
| protected boolean isCharacterPortableLocation(String fileAbsolutePath)
|
490 |
| { |
491 |
211
| Matcher matcher = PATH_PATTERN.matcher(fileAbsolutePath);
|
492 |
211
| if (matcher.find())
|
493 |
| { |
494 |
5
| log.warn("Cache loader location ( " + fileAbsolutePath + " ) contains one of these characters: '*' '<' '>' '|' '\"' '?'");
|
495 |
5
| log.warn("Directories containing these characters are illegal in some operative systems and could lead to portability issues");
|
496 |
5
| return false;
|
497 |
| } |
498 |
| |
499 |
206
| return true;
|
500 |
| } |
501 |
| |
502 |
1286
| protected boolean isCharacterPortableTree(Fqn fqn)
|
503 |
| { |
504 |
1286
| List elements = fqn.peekElements();
|
505 |
| |
506 |
1286
| for (Object anElement : elements)
|
507 |
| { |
508 |
| |
509 |
4123
| Matcher matcher = FQN_PATTERN.matcher(anElement.toString());
|
510 |
4123
| if (matcher.find())
|
511 |
| { |
512 |
11
| log.warn("One of the Fqn ( " + fqn + " ) elements contains one of these characters: '*' '<' '>' '|' '\"' '?' '\\' '/' ':' ");
|
513 |
11
| log.warn("Directories containing these characters are illegal in some operating systems and could lead to portability issues");
|
514 |
11
| return false;
|
515 |
| } |
516 |
| } |
517 |
| |
518 |
1275
| return true;
|
519 |
| } |
520 |
| |
521 |
1279
| protected boolean isLengthPortablePath(String absoluteFqnPath)
|
522 |
| { |
523 |
1279
| if (absoluteFqnPath.length() > 255)
|
524 |
| { |
525 |
1
| log.warn("The full absolute path to the fqn that you are trying to store is bigger than 255 characters, this could lead to problems in Windows systems: " + absoluteFqnPath);
|
526 |
1
| return false;
|
527 |
| } |
528 |
| |
529 |
1278
| return true;
|
530 |
| } |
531 |
| |
532 |
20504
| protected Object unmarshall(File from) throws Exception
|
533 |
| { |
534 |
20504
| FileInputStream fileIn = new FileInputStream(from);
|
535 |
20504
| ObjectInputStream input = ObjectSerializationFactory.createObjectInputStream(fileIn);
|
536 |
20504
| Object unmarshalledObj = getMarshaller().objectFromObjectStream(input);
|
537 |
20504
| input.close();
|
538 |
20504
| return unmarshalledObj;
|
539 |
| } |
540 |
| |
541 |
17137
| protected void marshall(Object obj, File to) throws Exception
|
542 |
| { |
543 |
17137
| FileOutputStream fileOut = new FileOutputStream(to);
|
544 |
17137
| ObjectOutputStream output = ObjectSerializationFactory.createObjectOutputStream(fileOut);
|
545 |
17137
| getMarshaller().objectToObjectStream(obj, output);
|
546 |
17137
| output.close();
|
547 |
| } |
548 |
| } |