1 package org.apache.jcs.auxiliary.disk.block;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.BufferedInputStream;
23 import java.io.BufferedOutputStream;
24 import java.io.EOFException;
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.Serializable;
32 import java.util.HashMap;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.concurrent.Executors;
36 import java.util.concurrent.ScheduledExecutorService;
37 import java.util.concurrent.ThreadFactory;
38 import java.util.concurrent.TimeUnit;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.jcs.auxiliary.disk.LRUMapJCS;
43 import org.apache.jcs.utils.timing.ElapsedTimer;
44
45
46
47
48
49
50 public class BlockDiskKeyStore
51 {
52
53 protected static final Log log = LogFactory.getLog( BlockDiskKeyStore.class );
54
55
56 private final BlockDiskCacheAttributes blockDiskCacheAttributes;
57
58
59 private Map<Serializable, int[]> keyHash;
60
61
62 private final File keyFile;
63
64
65 protected final String logCacheName;
66
67
68 private final String fileName;
69
70
71 private final int maxKeySize;
72
73
74 protected final BlockDiskCache blockDiskCache;
75
76
77
78
79 private static ScheduledExecutorService persistenceDaemon;
80
81
82
83
84
85
86
87 public BlockDiskKeyStore( BlockDiskCacheAttributes cacheAttributes, BlockDiskCache blockDiskCache )
88 {
89 this.blockDiskCacheAttributes = cacheAttributes;
90 this.logCacheName = "Region [" + this.blockDiskCacheAttributes.getCacheName() + "] ";
91 this.fileName = this.blockDiskCacheAttributes.getCacheName();
92 this.maxKeySize = cacheAttributes.getMaxKeySize();
93 this.blockDiskCache = blockDiskCache;
94
95 String rootDirName = cacheAttributes.getDiskPath();
96 File rootDirectory = new File( rootDirName );
97 rootDirectory.mkdirs();
98
99 if ( log.isInfoEnabled() )
100 {
101 log.info( logCacheName + "Cache file root directory [" + rootDirName + "]" );
102 }
103
104 this.keyFile = new File( rootDirectory, fileName + ".key" );
105
106 if ( log.isInfoEnabled() )
107 {
108 log.info( logCacheName + "Key File [" + this.keyFile.getAbsolutePath() + "]" );
109 }
110
111 if ( keyFile.length() > 0 )
112 {
113 loadKeys();
114
115 }
116 else
117 {
118 initKeyMap();
119 }
120
121
122
123 if ( this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds() > 0 )
124 {
125 if ( persistenceDaemon == null )
126 {
127 persistenceDaemon = Executors.newScheduledThreadPool(1, new MyThreadFactory());
128 }
129
130 persistenceDaemon
131 .scheduleAtFixedRate(new Runnable()
132 {
133 public void run()
134 {
135 saveKeys();
136 }
137 },
138 0,
139 this.blockDiskCacheAttributes.getKeyPersistenceIntervalSeconds(),
140 TimeUnit.SECONDS);
141 }
142 }
143
144
145
146
147
148 protected void saveKeys()
149 {
150 try
151 {
152 ElapsedTimer timer = new ElapsedTimer();
153 int numKeys = keyHash.size();
154 if ( log.isInfoEnabled() )
155 {
156 log.info( logCacheName + "Saving keys to [" + this.keyFile.getAbsolutePath() + "], key count ["
157 + numKeys + "]" );
158 }
159
160 synchronized (keyFile)
161 {
162 FileOutputStream fos = new FileOutputStream( keyFile );
163 BufferedOutputStream bos = new BufferedOutputStream( fos, 65536 );
164 ObjectOutputStream oos = new ObjectOutputStream( bos );
165 try
166 {
167
168 for (Map.Entry<Serializable, int[]> entry : keyHash.entrySet())
169 {
170 BlockDiskElementDescriptor descriptor = new BlockDiskElementDescriptor();
171 descriptor.setKey( entry.getKey() );
172 descriptor.setBlocks( entry.getValue() );
173
174 oos.writeObject( descriptor );
175 }
176 }
177 finally
178 {
179 oos.flush();
180 oos.close();
181 }
182 }
183
184 if ( log.isInfoEnabled() )
185 {
186 log.info( logCacheName + "Finished saving keys. It took " + timer.getElapsedTimeString() + " to store "
187 + numKeys + " keys. Key file length [" + keyFile.length() + "]" );
188 }
189 }
190 catch ( IOException e )
191 {
192 log.error( logCacheName + "Problem storing keys.", e );
193 }
194 }
195
196
197
198
199 protected void reset()
200 {
201 synchronized (keyFile)
202 {
203 clearMemoryMap();
204 saveKeys();
205 }
206
207 }
208
209
210
211
212 protected void clearMemoryMap()
213 {
214 this.keyHash.clear();
215 }
216
217
218
219
220 private void initKeyMap()
221 {
222 keyHash = null;
223 if ( maxKeySize >= 0 )
224 {
225 keyHash = new LRUMap( maxKeySize );
226 if ( log.isInfoEnabled() )
227 {
228 log.info( logCacheName + "Set maxKeySize to: '" + maxKeySize + "'" );
229 }
230 }
231 else
232 {
233
234 keyHash = new HashMap<Serializable, int[]>();
235
236 if ( log.isInfoEnabled() )
237 {
238 log.info( logCacheName + "Set maxKeySize to unlimited'" );
239 }
240 }
241 }
242
243
244
245
246
247 protected void loadKeys()
248 {
249 if ( log.isInfoEnabled() )
250 {
251 log.info( logCacheName + "Loading keys for " + keyFile.toString() );
252 }
253
254 try
255 {
256
257 initKeyMap();
258
259 HashMap<Serializable, int[]> keys = new HashMap<Serializable, int[]>();
260
261 synchronized (keyFile)
262 {
263 FileInputStream fis = new FileInputStream( keyFile );
264 BufferedInputStream bis = new BufferedInputStream( fis );
265 ObjectInputStream ois = new ObjectInputStream( bis );
266 try
267 {
268 while ( true )
269 {
270 BlockDiskElementDescriptor descriptor = (BlockDiskElementDescriptor) ois.readObject();
271 if ( descriptor != null )
272 {
273 keys.put( descriptor.getKey(), descriptor.getBlocks() );
274 }
275 }
276 }
277 catch ( EOFException eof )
278 {
279
280 }
281 finally
282 {
283 ois.close();
284 }
285 }
286
287 if ( !keys.isEmpty() )
288 {
289 keyHash.putAll( keys );
290
291 if ( log.isDebugEnabled() )
292 {
293 log.debug( logCacheName + "Found " + keys.size() + " in keys file." );
294 }
295
296 if ( log.isInfoEnabled() )
297 {
298 log.info( logCacheName + "Loaded keys from [" + fileName + "], key count: " + keyHash.size()
299 + "; up to " + maxKeySize + " will be available." );
300 }
301 }
302 }
303 catch ( Exception e )
304 {
305 log.error( logCacheName + "Problem loading keys for file " + fileName, e );
306 }
307 }
308
309
310
311
312
313
314 public Set<Map.Entry<Serializable, int[]>> entrySet()
315 {
316 return this.keyHash.entrySet();
317 }
318
319
320
321
322
323
324 public Set<Serializable> keySet()
325 {
326 return this.keyHash.keySet();
327 }
328
329
330
331
332
333
334 public int size()
335 {
336 return this.keyHash.size();
337 }
338
339
340
341
342
343
344
345 public int[] get( Object key )
346 {
347 return this.keyHash.get( key );
348 }
349
350
351
352
353
354
355
356 public void put( Serializable key, int[] value )
357 {
358 this.keyHash.put( key, value );
359 }
360
361
362
363
364
365
366
367 public int[] remove( Serializable key )
368 {
369 return this.keyHash.remove( key );
370 }
371
372
373
374
375
376 public class LRUMap
377 extends LRUMapJCS<Serializable, int[]>
378 {
379
380 private static final long serialVersionUID = 4955079991472142198L;
381
382
383
384
385 public String tag = "orig";
386
387
388
389
390 public LRUMap()
391 {
392 super();
393 }
394
395
396
397
398 public LRUMap( int maxKeySize )
399 {
400 super( maxKeySize );
401 }
402
403
404
405
406
407
408
409
410 @Override
411 protected void processRemovedLRU( Serializable key, int[] value )
412 {
413 blockDiskCache.freeBlocks( value );
414 if ( log.isDebugEnabled() )
415 {
416 log.debug( logCacheName + "Removing key: [" + key + "] from key store." );
417 log.debug( logCacheName + "Key store size: [" + super.size() + "]." );
418 }
419 }
420 }
421
422
423
424
425
426 protected static class MyThreadFactory
427 implements ThreadFactory
428 {
429
430
431
432
433
434
435
436 public Thread newThread( Runnable runner )
437 {
438 Thread t = new Thread( runner );
439 String oldName = t.getName();
440 t.setName( "JCS-BlockDiskKeyStore-" + oldName );
441 t.setDaemon( true );
442 t.setPriority( Thread.MIN_PRIORITY );
443 return t;
444 }
445 }
446 }