KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > util > FilePersistentStore


1
2 /*
3  * Enhydra Java Application Server Project
4  *
5  * The contents of this file are subject to the Enhydra Public License
6  * Version 1.1 (the "License"); you may not use this file except in
7  * compliance with the License. You may obtain a copy of the License on
8  * the Enhydra web site ( http://www.enhydra.org/ ).
9  *
10  * Software distributed under the License is distributed on an "AS IS"
11  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
12  * the License for the specific terms governing rights and limitations
13  * under the License.
14  *
15  * The Initial Developer of the Enhydra Application Server is Lutris
16  * Technologies, Inc. The Enhydra Application Server and portions created
17  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
18  * All Rights Reserved.
19  *
20  * Contributor(s):
21  *
22  * $Id: FilePersistentStore.java,v 1.2 2005/03/24 10:51:25 slobodan Exp $
23  */

24
25 package com.lutris.util;
26 import java.io.File JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.FileOutputStream JavaDoc;
29 import java.io.FilenameFilter JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.io.InputStream JavaDoc;
32 import java.io.ObjectInputStream JavaDoc;
33 import java.io.ObjectOutputStream JavaDoc;
34 import java.io.ObjectStreamClass JavaDoc;
35 import java.io.Serializable JavaDoc;
36 import java.io.StreamCorruptedException JavaDoc;
37 import java.lang.reflect.Constructor JavaDoc;
38 import java.util.NoSuchElementException JavaDoc;
39
40 /**
41  * File system implementation of PersistentStore.
42  *
43  * @author Kyle Clark
44  */

45 public class FilePersistentStore implements PersistentStore {
46
47     private final String JavaDoc DEFAULT_DIR_PROPERTY = "user.dir";
48     private final String JavaDoc FILE_SEPARATOR_PROPERTY = "file.separator";
49     private final String JavaDoc SERIAL_SUFFIX = ".srl"; // suffix for serialized objects.
50

51
52     /**
53      * Inner class that implements keys enumeration for this object.
54      */

55     private class FilePersistentStoreKeys implements java.util.Enumeration JavaDoc {
56
57         private String JavaDoc [] keys;
58         private int idx = 0;
59
60         /**
61          * Constructor.
62          */

63         FilePersistentStoreKeys() {
64             keys = new String JavaDoc[0];
65         }
66
67         /**
68          * Constructor.
69          *
70          * @param keys
71          * The keys.
72          */

73         FilePersistentStoreKeys(String JavaDoc [] keys) {
74             this.keys = keys;
75         }
76
77
78         /**
79          * Tests if this enumeration contains more keys.
80          *
81          * @return
82          * True if it does contain more elements, false otherwise.
83          */

84         public boolean hasMoreElements() {
85             return (idx < keys.length);
86         }
87
88         /**
89          * Returns the next key of this enumeration.
90          *
91          * @return
92          * The next key.
93          * @exception java.util.NoSuchElementException
94          * if there are no more keys in this enumeration.
95          */

96         public Object JavaDoc nextElement() throws NoSuchElementException JavaDoc {
97             if (!hasMoreElements())
98                 throw new java.util.NoSuchElementException JavaDoc("no more elements");
99
100             String JavaDoc k = keys[idx];
101             idx++;
102             return k;
103         }
104     }
105
106     /**
107      * Inner class that implements a filename filter.
108      */

109     private class FileStoreFilter implements FilenameFilter JavaDoc {
110         private String JavaDoc suffix = "";
111         FileStoreFilter(String JavaDoc suffix) {
112             this.suffix = suffix;
113         }
114         public boolean accept(File JavaDoc dir, String JavaDoc name) {
115             return name.endsWith(suffix);
116         }
117     }
118
119
120     /**
121      * Path separator used by operating system.
122      */

123     private String JavaDoc fileSeparator;
124
125     /**
126      * Directory (pathname) where objects are to be
127      * stored/retrieved. The default is the
128      * current directory.
129      */

130     private String JavaDoc storeDirectory;
131
132     /**
133      * The class loader used when reading in serialized data.
134      */

135     private Constructor JavaDoc objectInputStreamConstructor;
136
137     /**
138      * The loader that should be used to load serialized data.
139      */

140     private ClassLoader JavaDoc loader;
141
142     /**
143      * Public constructor. Sets the <code>storeDirectory</code>,
144      * the directory (repository) where objects
145      * are stored/retreived to <code>/tmp</code>.
146      *
147      * @exception java.io.IOException
148      * If <code>/tmp</code>
149      * already exists but is not a directory. Or if
150      * <code>/tmp</code> does not exist and could
151      * not be created.
152      * @exception java.lang.SecurityException
153      * If a security
154      * manager is running but <code>/tmp</code>
155      * could not be created due to inappropriate
156      * permissions.
157      */

158     public FilePersistentStore()
159         throws PersistentStoreException {
160         init(null, null);
161     }
162
163     /**
164      * Public constructor. Sets the <code>storeDirectory</code>,
165      * the directory (repository) where objects
166      * are stored/retreived to <code>/tmp</code>.
167      *
168      * @param loader the class loader to use when reading
169      * in serialized data.
170      * @exception java.io.IOException
171      * If <code>/tmp</code>
172      * already exists but is not a directory. Or if
173      * <code>/tmp</code> does not exist and could
174      * not be created.
175      * @exception java.lang.SecurityException
176      * If a security
177      * manager is running but <code>/tmp</code>
178      * could not be created due to inappropriate
179      * permissions.
180      */

181     public FilePersistentStore(ClassLoader JavaDoc loader)
182         throws IOException JavaDoc, SecurityException JavaDoc, PersistentStoreException {
183         init(null, loader);
184     }
185
186     /**
187      * Public constructor that allows one to specify
188      * the directory (repository)
189      * where objects are stored/retrieved.
190      * If the specified directory doesn't exist then it is created.
191      *
192      * @param storeDirectory
193      * The repository (directory) for storing and retrieving objects.
194      * @exception java.io.IOException
195      * If <code>storeDirectory</code>
196      * already exists but is not a directory. Or if the
197      * <code>storeDirectory</code> does not exist and could
198      * not be created.
199      * @exception java.lang.SecurityException
200      * If a security
201      * manager is running but <code>storeDirectory</code>
202      * could not be created due to inappropriate
203      * permissions.
204      */

205     public FilePersistentStore(String JavaDoc storeDirectory)
206         throws PersistentStoreException {
207         init(storeDirectory, null);
208     }
209
210     /**
211      * Public constructor that allows one to specify
212      * the directory (repository)
213      * where objects are stored/retrieved.
214      * If the specified directory doesn't exist then it is created.
215      *
216      * @param storeDirectory
217      * The repository (directory) for storing and retrieving objects.
218      * @exception java.io.IOException
219      * If <code>storeDirectory</code>
220      * already exists but is not a directory. Or if the
221      * <code>storeDirectory</code> does not exist and could
222      * not be created.
223      * @exception java.lang.SecurityException
224      * If a security
225      * manager is running but <code>storeDirectory</code>
226      * could not be created due to inappropriate
227      * permissions.
228      */

229     public FilePersistentStore(String JavaDoc storeDirectory, ClassLoader JavaDoc loader)
230         throws PersistentStoreException {
231         init(storeDirectory, loader);
232     }
233
234     /**
235      * Gets a reference to the object input stream constructor.
236      *
237      * @param storeDirectory
238      * The repository (directory) for storing and retrieving objects.
239      * @exception java.io.IOException
240      * If <code>storeDirectory</code>
241      * already exists but is not a directory. Or if the
242      * <code>storeDirectory</code> does not exist and could
243      * not be created.
244      * @exception java.lang.SecurityException
245      * If a security
246      * manager is running but <code>storeDirectory</code>
247      * could not be created due to inappropriate
248      * permissions.
249      */

250     private void init(String JavaDoc storeDirectory, ClassLoader JavaDoc loader)
251         throws PersistentStoreException {
252         try {
253             this.loader = loader;
254             fileSeparator = System.getProperty(FILE_SEPARATOR_PROPERTY);
255             if (storeDirectory == null) {
256                 setStoreDirectory(fileSeparator + "tmp");
257             } else {
258                 setStoreDirectory(storeDirectory);
259             }
260         } catch (Exception JavaDoc e) {
261             throw new PersistentStoreException(e);
262         }
263     }
264
265     /**
266      * Sets the location (directory) from where objects are
267      * retrieved/archived. If the specified directory doesn't
268      * exist then it is created.
269      *
270      * @param storeDirectory The repository (directory) for
271      * storing and retrieving objects.
272      * @exception java.io.IOException If <code>storeDirectory</code>
273      * already exists but is not a directory. Or if the
274      * <code>storeDirectory</code> does not exist and could
275      * not be created.
276      * @exception java.lang.SecurityException If a security
277      * manager is running but <code>storeDirectory</code>
278      * could not be created because due to inappropriate
279      * permissions.
280      */

281     private void setStoreDirectory(String JavaDoc storeDirectory)
282         throws IOException JavaDoc, SecurityException JavaDoc {
283         File JavaDoc f = new File JavaDoc(storeDirectory);
284         if (f.exists() && !f.isDirectory())
285             throw new IOException JavaDoc(storeDirectory +
286                                   " already exists but is not a directory.");
287         if (!f.exists() && !f.mkdirs())
288             throw new IOException JavaDoc("Unable to create directory " +
289                                   storeDirectory);
290         this.storeDirectory = storeDirectory;
291     }
292
293
294     /**
295      * Returns the location where objects are
296      * stored/retrieved.
297      *
298      * @return The directory (repository) where
299      * objects are stored/retrieved.
300      */

301     public String JavaDoc getStoreDirectory() {
302         return this.storeDirectory;
303     }
304
305     /**
306      * Method that calculates the absolute path to the
307      * stored object.
308      *
309      * @param key The user for whom the archive name
310      * is calculated.
311      * @return The filename associated w/ key.
312      */

313     private String JavaDoc filename(String JavaDoc key) {
314         return storeDirectory + fileSeparator + convertKeyToHex(key) + SERIAL_SUFFIX;
315     }
316
317     /**
318      * Hexadecimal characters corresponding to each half byte value.
319      */

320     private static final char[] HexChars = {
321     '0', '1', '2', '3', '4', '5', '6', '7',
322     '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
323     };
324
325
326     /**
327      * Converts an arbitrary string to ASCII hexadecimal string
328      * form, with two hex characters corresponding to each byte. The
329      * length of the resultant string in characters will be twice the
330      * length of the original string.
331      *
332      * @param key
333      * The string to convert to ASCII hex form.
334      * @return
335      * An ASCII hexadecimal numeric string representing the
336      * original string.
337      */

338     private String JavaDoc convertKeyToHex(String JavaDoc key) {
339         byte [] bytes = key.getBytes();
340     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
341     int i;
342     for (i=0; i < bytes.length; i++) {
343         sb.append(HexChars[(bytes[i] >> 4) & 0xf]);
344         sb.append(HexChars[bytes[i] & 0xf]);
345     }
346     return new String JavaDoc(sb);
347     }
348
349     /**
350      * Converts an arbitrary ASCII hexadecimal string,
351      * with two hex characters corresponding to each byte, into
352      * a string.
353      *
354      * @param hex
355      * The hexadecimal encoded string to convert.
356      * @return
357      * The original string.
358      */

359     private String JavaDoc convertHexToKey(String JavaDoc hex) {
360         char [] chars = new char[hex.length()];
361         hex.getChars(0, hex.length(), chars, 0);
362     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
363     for (int i=0; i<chars.length/2; i++) {
364             int idx = 2*i;
365             sb.append((char)((hexCharToByte(chars[idx])<<4) +
366                              (hexCharToByte(chars[idx+1]))));
367     }
368     return new String JavaDoc(sb);
369     }
370
371     /**
372      * Method that maps a hexadecimal character to a byte value.
373      *
374      * @param c
375      * The character to map.
376      */

377     private byte hexCharToByte(char c) {
378         switch (c) {
379         case '1':
380             return (byte)1;
381         case '2':
382             return (byte)2;
383         case '3':
384             return (byte)3;
385         case '4':
386             return (byte)4;
387         case '5':
388             return (byte)5;
389         case '6':
390             return (byte)6;
391         case '7':
392             return (byte)7;
393         case '8':
394             return (byte)8;
395         case '9':
396             return (byte)9;
397         case 'a':
398         case 'A':
399             return (byte)10;
400         case 'b':
401         case 'B':
402             return (byte)11;
403         case 'c':
404         case 'C':
405             return (byte)12;
406         case 'd':
407         case 'D':
408             return (byte)13;
409         case 'e':
410         case 'E':
411             return (byte)14;
412         case 'f':
413         case 'F':
414             return (byte)15;
415         case '0':
416         default:
417             return (byte)0;
418         }
419     }
420
421     /**
422      * Method to store and object (persistent).
423      *
424      * @param key
425      * The key by which to identify the stored object.
426      * The key name has some system constraints. Its length
427      * must not exceed half the maximum file name length on
428      * the system. If it does, an exception is thrown.
429      * @param obj
430      * The serializable object to store.
431      * @exception PersistentStoreException
432      * if the object cannot cannot be stored.
433      */

434     public void store(String JavaDoc key, Serializable JavaDoc obj)
435         throws PersistentStoreException {
436         if (exists(key))
437             throw new
438                 PersistentStoreException("An object is already stored as " +
439                                          key);
440         FileOutputStream JavaDoc out = null;
441         try {
442             out = new FileOutputStream JavaDoc(filename(key));
443             ObjectOutputStream JavaDoc objOut = new ObjectOutputStream JavaDoc(out);
444             objOut.writeObject(obj);
445             objOut.flush();
446             out.close();
447         }
448         catch (Exception JavaDoc ex) {
449             if (out != null) {
450                 try { out.close(); } catch (Exception JavaDoc e) {}
451             }
452             delete(key);
453             throw new PersistentStoreException(ex);
454         }
455     }
456     
457
458     /**
459      * Method to retrieve a stored object.
460      *
461      * @param key
462      * The key of the object that is to be retreived.
463      * @return
464      * The stored object. If an object is not stored under key,
465      * then <code>null</code> is returned.
466      * @see #remove
467      * @exception PersistentStoreException
468      * if the object could not be retrieved.
469      */

470     public Object JavaDoc retrieve(String JavaDoc key) throws PersistentStoreException {
471         if (!exists(key)) {
472             return null;
473         }
474         try {
475             FileInputStream JavaDoc in = new FileInputStream JavaDoc(filename(key));
476             LoaderObjectInputStream objIn = new LoaderObjectInputStream(in, loader);
477             Object JavaDoc obj = objIn.readObject();
478             objIn.close();
479             return obj;
480         }
481         catch (Exception JavaDoc ex) {
482             throw new PersistentStoreException(ex);
483         }
484     }
485
486
487     /**
488      * Method to query if an an object is stored.
489      *
490      * @param key
491      * The key by which to identify the stored object.
492      * @return
493      * True if an object is stored under key.
494      * @exception PersistentStoreException
495      * If The exsitence of object could not be determined.
496      */

497     public boolean exists(String JavaDoc key)
498         throws PersistentStoreException {
499         File JavaDoc f = new File JavaDoc(filename(key));
500         return f.exists();
501     }
502
503
504     /**
505      * Method to simultaneously retrieve and remove an
506      * object from persistent store. If an object is not
507      * stored under key, then null is returned.
508      *
509      * @param key
510      * The key by which to identify the stored object
511      * that is to be removed.
512      * @return
513      * The object that has been removed.
514      * @exception PersistentStoreException
515      * If the object could not be retrieved before being deleted.
516      */

517     public Object JavaDoc remove(String JavaDoc key) throws PersistentStoreException {
518         Object JavaDoc obj = retrieve(key);
519         delete(key);
520         return obj;
521     }
522
523
524     /**
525      * Method to delete a a key. Any objects stored under
526      * key are also removed. If key is not defined, then
527      * this method does nothing.
528      *
529      * @param key
530      * The key to remove.
531      */

532     public void delete(String JavaDoc key) {
533         File JavaDoc f = new File JavaDoc(filename(key));
534         if (f.exists()) {
535             f.delete();
536         }
537     }
538
539
540     /**
541      * Method that returns an enumration of the keys
542      * of this persistent store.
543      *
544      * @exception com.lutris.util.PersistentStoreException
545      * if the enumeration could not be determined.
546      */

547     public java.util.Enumeration JavaDoc keys() throws PersistentStoreException {
548         File JavaDoc dir = new File JavaDoc(storeDirectory);
549         FileStoreFilter filter = new FileStoreFilter(SERIAL_SUFFIX);
550         String JavaDoc [] hexKeys = dir.list(filter);
551         String JavaDoc [] keys = new String JavaDoc [hexKeys.length];
552         for (int idx=0; idx<hexKeys.length; idx++) {
553             // Strip off the suffix
554
int suffixIdx = hexKeys[idx].lastIndexOf(SERIAL_SUFFIX);
555             if (suffixIdx >= 0)
556                 hexKeys[idx] = hexKeys[idx].substring(0, suffixIdx);
557             // Get the real key name
558
keys[idx] = convertHexToKey(hexKeys[idx]);
559         }
560
561         return new FilePersistentStoreKeys(keys);
562     }
563
564 }
565
566 // Internal class that can load objects with the load we specify.
567
class LoaderObjectInputStream extends ObjectInputStream JavaDoc {
568
569     private ClassLoader JavaDoc loader;
570
571     public LoaderObjectInputStream(InputStream JavaDoc in, ClassLoader JavaDoc loader)
572     throws IOException JavaDoc, StreamCorruptedException JavaDoc {
573         super(in);
574         this.loader = loader;
575     }
576
577     /**
578      * Subclasses may implement this method to allow classes to be
579      * fetched from an alternate source.
580      *
581      * The corresponding method in ObjectOutputStream is
582      * annotateClass. This method will be invoked only once for each
583      * unique class in the stream. This method can be implemented by
584      * subclasses to use an alternate loading mechanism but must
585      * return a Class object. Once returned, the serialVersionUID of the
586      * class is compared to the serialVersionUID of the serialized class.
587      * If there is a mismatch, the deserialization fails and an exception
588      * is raised. <p>
589      *
590      * By default the class name is resolved relative to the class
591      * that called readObject. <p>
592      *
593      * @exception ClassNotFoundException If class of
594      * a serialized object cannot be found.
595      * @since JDK1.1
596      */

597     protected Class JavaDoc resolveClass(ObjectStreamClass JavaDoc v)
598     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
599         if (loader != null) {
600             return loader.loadClass(v.getName());
601         } else {
602             return super.resolveClass(v);
603         }
604     }
605
606 }
607
Popular Tags