Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/lutris/util/FilePersistentStore.java


1   /*
2    * Enhydra Java Application Server Project
3    * 
4    * The contents of this file are subject to the Enhydra Public License
5    * Version 1.1 (the "License"); you may not use this file except in
6    * compliance with the License. You may obtain a copy of the License on
7    * the Enhydra web site ( http://www.enhydra.org/ ).
8    * 
9    * Software distributed under the License is distributed on an "AS IS"
10   * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
11   * the License for the specific terms governing rights and limitations
12   * under the License.
13   * 
14   * The Initial Developer of the Enhydra Application Server is Lutris
15   * Technologies, Inc. The Enhydra Application Server and portions created
16   * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17   * All Rights Reserved.
18   * 
19   * Contributor(s):
20   * 
21   * $Id: FilePersistentStore.java,v 1.10.12.1 2000/10/19 17:58:53 jasona Exp $
22   */
23  
24  package com.lutris.util;
25  import java.io.*;
26  import java.util.NoSuchElementException;
27  import java.lang.reflect.Constructor;
28  
29  /**
30   * File system implementation of PersistentStore.
31   *
32   * @author Kyle Clark
33   */
34  public class FilePersistentStore implements PersistentStore {
35  
36      private final String DEFAULT_DIR_PROPERTY = "user.dir";
37      private final String FILE_SEPARATOR_PROPERTY = "file.separator";
38      private final String SERIAL_SUFFIX = ".srl"; // suffix for serialized objects.
39  
40  
41      /**
42       * Inner class that implements keys enumeration for this object.
43       */
44      private class FilePersistentStoreKeys implements java.util.Enumeration {
45  
46          private String [] keys;
47          private int idx = 0;
48  
49          /**
50           * Constructor.
51           */
52          FilePersistentStoreKeys() {
53              keys = new String[0];
54          }
55  
56          /**
57           * Constructor.
58           *
59           * @param keys
60           *   The keys.
61           */
62          FilePersistentStoreKeys(String [] keys) {
63              this.keys = keys;
64          }
65  
66  
67          /**
68           * Tests if this enumeration contains more keys.
69           *
70           * @return
71           *   True if it does contain more elements, false otherwise.
72           */
73          public boolean hasMoreElements() {
74              return (idx < keys.length);
75          }
76  
77          /**
78           * Returns the next key of this enumeration.
79           *
80           * @return
81           *   The next key.
82           * @exception java.util.NoSuchElementException
83           *   if there are no more keys in this enumeration.
84           */
85          public Object nextElement() throws NoSuchElementException {
86              if (!hasMoreElements())
87                  throw new java.util.NoSuchElementException("no more elements");
88  
89              String k = keys[idx];
90              idx++;
91              return k;
92          }
93      }
94  
95      /**
96       * Inner class that implements a filename filter.
97       */
98      private class FileStoreFilter implements FilenameFilter {
99          private String suffix = "";
100         FileStoreFilter(String suffix) {
101             this.suffix = suffix;
102         }
103         public boolean accept(File dir, String name) {
104             return name.endsWith(suffix);
105         }
106     }
107 
108 
109     /**
110      * Path separator used by operating system.
111      */
112     private String fileSeparator;
113 
114     /**
115      * Directory (pathname) where objects are to be
116      * stored/retrieved.  The default is the
117      * current directory.
118      */
119     private String storeDirectory;
120 
121     /**
122      * The class loader used when reading in serialized data.
123      */
124     private Constructor objectInputStreamConstructor;
125 
126     /**
127      * The loader that should be used to load serialized data.
128      */
129     private ClassLoader loader;
130 
131     /**
132      * Public constructor.  Sets the <code>storeDirectory</code>,
133      * the directory (repository) where objects
134      * are stored/retreived to <code>/tmp</code>.
135      *
136      * @exception java.io.IOException
137      *   If <code>/tmp</code>
138      *   already exists but is not a directory.  Or if 
139      *   <code>/tmp</code> does not exist and could
140      *   not be created.
141      * @exception java.lang.SecurityException
142      *   If a security
143      *   manager is running but <code>/tmp</code>
144      *   could not be created due to inappropriate
145      *   permissions.
146      */
147     public FilePersistentStore()
148         throws PersistentStoreException {
149         init(null, null);
150     }
151 
152     /**
153      * Public constructor.  Sets the <code>storeDirectory</code>,
154      * the directory (repository) where objects
155      * are stored/retreived to <code>/tmp</code>.
156      *
157      * @param loader the class loader to use when reading
158      *   in serialized data.
159      * @exception java.io.IOException
160      *   If <code>/tmp</code>
161      *   already exists but is not a directory.  Or if 
162      *   <code>/tmp</code> does not exist and could
163      *   not be created.
164      * @exception java.lang.SecurityException
165      *   If a security
166      *   manager is running but <code>/tmp</code>
167      *   could not be created due to inappropriate
168      *   permissions.
169      */
170     public FilePersistentStore(ClassLoader loader)
171         throws IOException, SecurityException, PersistentStoreException {
172         init(null, loader);
173     }
174 
175     /**
176      * Public constructor that allows one to specify
177      * the directory (repository)
178      * where objects are stored/retrieved.
179      * If the specified directory doesn't exist then it is created.
180      *
181      * @param storeDirectory
182      *   The repository (directory) for storing and retrieving objects.
183      * @exception java.io.IOException
184      *   If <code>storeDirectory</code>
185      *   already exists but is not a directory.  Or if the
186      *   <code>storeDirectory</code> does not exist and could
187      *   not be created.
188      * @exception java.lang.SecurityException
189      *   If a security
190      *   manager is running but <code>storeDirectory</code>
191      *   could not be created due to inappropriate
192      *   permissions.
193      */
194     public FilePersistentStore(String storeDirectory)
195         throws PersistentStoreException {
196         init(storeDirectory, null);
197     }
198 
199     /**
200      * Public constructor that allows one to specify
201      * the directory (repository)
202      * where objects are stored/retrieved.
203      * If the specified directory doesn't exist then it is created.
204      *
205      * @param storeDirectory
206      *   The repository (directory) for storing and retrieving objects.
207      * @exception java.io.IOException
208      *   If <code>storeDirectory</code>
209      *   already exists but is not a directory.  Or if the
210      *   <code>storeDirectory</code> does not exist and could
211      *   not be created.
212      * @exception java.lang.SecurityException
213      *   If a security
214      *   manager is running but <code>storeDirectory</code>
215      *   could not be created due to inappropriate
216      *   permissions.
217      */
218     public FilePersistentStore(String storeDirectory, ClassLoader loader)
219         throws PersistentStoreException {
220         init(storeDirectory, loader);
221     }
222 
223     /**
224      * Gets a reference to the object input stream constructor.
225      *
226      * @param storeDirectory
227      *   The repository (directory) for storing and retrieving objects.
228      * @exception java.io.IOException
229      *   If <code>storeDirectory</code>
230      *   already exists but is not a directory.  Or if the
231      *   <code>storeDirectory</code> does not exist and could
232      *   not be created.
233      * @exception java.lang.SecurityException
234      *   If a security
235      *   manager is running but <code>storeDirectory</code>
236      *   could not be created due to inappropriate
237      *   permissions.
238      */
239     private void init(String storeDirectory, ClassLoader loader)
240         throws PersistentStoreException {
241         try {
242             this.loader = loader;
243             fileSeparator = System.getProperty(FILE_SEPARATOR_PROPERTY);
244             if (storeDirectory == null) {
245                 setStoreDirectory(fileSeparator + "tmp");
246             } else {
247                 setStoreDirectory(storeDirectory);
248             }
249         } catch (Exception e) {
250             throw new PersistentStoreException(e);
251         }
252     }
253 
254     /**
255      * Sets the location (directory) from where objects are
256      * retrieved/archived.  If the specified directory doesn't
257      * exist then it is created.
258      *
259      * @param storeDirectory The repository (directory) for
260      *   storing and retrieving objects.
261      * @exception java.io.IOException If <code>storeDirectory</code>
262      *   already exists but is not a directory.  Or if the
263      *   <code>storeDirectory</code> does not exist and could
264      *   not be created.
265      * @exception java.lang.SecurityException If a security
266      *   manager is running but <code>storeDirectory</code>
267      *   could not be created because due to inappropriate
268      *   permissions.
269      */
270     private void setStoreDirectory(String storeDirectory)
271         throws IOException, SecurityException {
272         File f = new File(storeDirectory);
273         if (f.exists() && !f.isDirectory())
274             throw new IOException(storeDirectory +
275                                   " already exists but is not a directory.");
276         if (!f.exists() && !f.mkdirs())
277             throw new IOException("Unable to create directory " +
278                                   storeDirectory);
279         this.storeDirectory = storeDirectory;
280     }
281 
282 
283     /**
284      * Returns the location where objects are
285      * stored/retrieved.
286      *
287      * @return The directory (repository) where 
288      *   objects are stored/retrieved.
289      */
290     public String getStoreDirectory() {
291         return this.storeDirectory;
292     }
293 
294     /**
295      * Method that calculates the absolute path to the
296      * stored object.
297      *
298      * @param key The user for whom the archive name
299      *   is calculated.
300      * @return The filename associated w/ key.
301      */
302     private String filename(String key) {
303         return storeDirectory + fileSeparator + convertKeyToHex(key) + SERIAL_SUFFIX;
304     }
305 
306     /**
307      * Hexadecimal characters corresponding to each half byte value.
308      */
309     private static final char[] HexChars = {
310   '0', '1', '2', '3', '4', '5', '6', '7',
311   '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
312     };
313 
314 
315     /**
316      * Converts an arbitrary string to ASCII hexadecimal string
317      * form, with two hex characters corresponding to each byte.  The
318      * length of the resultant string in characters will be twice the
319      * length of the original string.
320      * 
321      * @param key
322      *   The string to convert to ASCII hex form.
323      * @return
324      *   An ASCII hexadecimal numeric string representing the
325      *    original string.
326      */
327     private String convertKeyToHex(String key) {
328         byte [] bytes = key.getBytes();
329   StringBuffer sb = new StringBuffer();
330   int i;
331   for (i=0; i < bytes.length; i++) {
332       sb.append(HexChars[(bytes[i] >> 4) & 0xf]);
333       sb.append(HexChars[bytes[i] & 0xf]);
334   }
335   return new String(sb);
336     }
337 
338     /**
339      * Converts an arbitrary ASCII hexadecimal string,
340      * with two hex characters corresponding to each byte, into
341      * a string.
342      * 
343      * @param hex
344      *   The hexadecimal encoded string to convert.
345      * @return
346      *   The original string.
347      */
348     private String convertHexToKey(String hex) {
349         char [] chars = new char[hex.length()];
350         hex.getChars(0, hex.length(), chars, 0);
351   StringBuffer sb = new StringBuffer();
352   for (int i=0; i<chars.length/2; i++) {
353             int idx = 2*i;
354             sb.append((char)((hexCharToByte(chars[idx])<<4) +
355                              (hexCharToByte(chars[idx+1]))));
356   }
357   return new String(sb);
358     }
359 
360     /**
361      * Method that maps a hexadecimal character to a byte value.
362      *
363      * @param c
364      *   The character to map.
365      */
366     private byte hexCharToByte(char c) {
367         switch (c) {
368         case '1':
369             return (byte)1;
370         case '2':
371             return (byte)2;
372         case '3':
373             return (byte)3;
374         case '4':
375             return (byte)4;
376         case '5':
377             return (byte)5;
378         case '6':
379             return (byte)6;
380         case '7':
381             return (byte)7;
382         case '8':
383             return (byte)8;
384         case '9':
385             return (byte)9;
386         case 'a':
387         case 'A':
388             return (byte)10;
389         case 'b':
390         case 'B':
391             return (byte)11;
392         case 'c':
393         case 'C':
394             return (byte)12;
395         case 'd':
396         case 'D':
397             return (byte)13;
398         case 'e':
399         case 'E':
400             return (byte)14;
401         case 'f':
402         case 'F':
403             return (byte)15;
404         case '0':
405         default:
406             return (byte)0;
407         }
408     }
409 
410     /**
411      * Method to store and object (persistent).
412      *
413      * @param key
414      *   The key by which to identify the stored object.
415      *   The key name has some system constraints.  Its length
416      *   must not exceed half the maximum file name length on
417      *   the system.  If it does, an exception is thrown.
418      * @param obj
419      *   The serializable object to store.
420      * @exception PersistentStoreException
421      *   if the object cannot cannot be stored.
422      */
423     public void store(String key, Serializable obj)
424         throws PersistentStoreException {
425         if (exists(key))
426             throw new
427                 PersistentStoreException("An object is already stored as " +
428                                          key);
429         FileOutputStream out = null;
430         try {
431             out = new FileOutputStream(filename(key));
432             ObjectOutputStream objOut = new ObjectOutputStream(out);
433             objOut.writeObject(obj);
434             objOut.flush();
435             out.close();
436         }
437         catch (Exception ex) {
438             if (out != null) {
439                 try { out.close(); } catch (Exception e) {}
440             }
441             delete(key);
442             throw new PersistentStoreException(ex);
443         }
444     }
445     
446 
447     /**
448      * Method to retrieve a stored object.
449      *
450      * @param key
451      *   The key of the object that is to be retreived.
452      * @return
453      *   The stored object.  If an object is not stored under key,
454      *   then <code>null</code> is returned.
455      * @see #remove
456      * @exception PersistentStoreException
457      *   if the object could not be retrieved.
458      */
459     public Object retrieve(String key) throws PersistentStoreException {
460         if (!exists(key)) {
461             return null;
462         }
463         try {
464             FileInputStream in = new FileInputStream(filename(key));
465             LoaderObjectInputStream objIn = new LoaderObjectInputStream(in, loader);
466             Object obj = objIn.readObject();
467             objIn.close();
468             return obj;
469         }
470         catch (Exception ex) {
471             throw new PersistentStoreException(ex);
472         }
473     }
474 
475 
476     /**
477      * Method to query if an an object is stored.
478      *
479      * @param key
480      *   The key by which to identify the stored object.
481      * @return
482      *   True if an object is stored under key.
483      * @exception PersistentStoreException
484      *   If The exsitence of object could not be determined.
485      */
486     public boolean exists(String key)
487         throws PersistentStoreException {
488         File f = new File(filename(key));
489         return f.exists();
490     }
491 
492 
493     /**
494      * Method to simultaneously retrieve and remove an
495      * object from persistent store.  If an object is not
496      * stored under key, then null is returned.
497      *
498      * @param key
499      *   The key by which to identify the stored object
500      *   that is to be removed.
501      * @return
502      *   The object that has been removed.
503      * @exception PersistentStoreException
504      *   If the object could not be retrieved before being deleted.
505      */
506     public Object remove(String key) throws PersistentStoreException {
507         Object obj = retrieve(key);
508         delete(key);
509         return obj;
510     }
511 
512 
513     /**
514      * Method to delete a a key.  Any objects stored under
515      * key are also removed.  If key is not defined, then
516      * this method does nothing.
517      *
518      * @param key
519      *   The key to remove.
520      */
521     public void delete(String key) {
522         File f = new File(filename(key));
523         if (f.exists()) {
524             f.delete();
525         }
526     }
527 
528 
529     /**
530      * Method that returns an enumration of the keys
531      * of this persistent store.
532      *
533      * @exception com.lutris.util.PersistentStoreException
534      *   if the enumeration could not be determined.
535      */
536     public java.util.Enumeration keys() throws PersistentStoreException {
537         File dir = new File(storeDirectory);
538         FileStoreFilter filter = new FileStoreFilter(SERIAL_SUFFIX);
539         String [] hexKeys = dir.list(filter);
540         String [] keys = new String [hexKeys.length];
541         for (int idx=0; idx<hexKeys.length; idx++) {
542             // Strip off the suffix
543             int suffixIdx = hexKeys[idx].lastIndexOf(SERIAL_SUFFIX);
544             if (suffixIdx >= 0)
545                 hexKeys[idx] = hexKeys[idx].substring(0, suffixIdx);
546             // Get the real key name
547             keys[idx] = convertHexToKey(hexKeys[idx]);
548         }
549 
550         return new FilePersistentStoreKeys(keys);
551     }
552 
553 }
554 
555 // Internal class that can load objects with the load we specify.
556 class LoaderObjectInputStream extends ObjectInputStream {
557 
558     private ClassLoader loader;
559 
560     public LoaderObjectInputStream(InputStream in, ClassLoader loader)
561   throws IOException, StreamCorruptedException {
562         super(in);
563         this.loader = loader;
564     }
565 
566     /**
567      * Subclasses may implement this method to allow classes to be
568      * fetched from an alternate source. 
569      *
570      * The corresponding method in ObjectOutputStream is
571      * annotateClass.  This method will be invoked only once for each
572      * unique class in the stream.  This method can be implemented by
573      * subclasses to use an alternate loading mechanism but must
574      * return a Class object.  Once returned, the serialVersionUID of the
575      * class is compared to the serialVersionUID of the serialized class.
576      * If there is a mismatch, the deserialization fails and an exception
577      * is raised. <p>
578      *
579      * By default the class name is resolved relative to the class
580      * that called readObject. <p>
581      *
582      * @exception ClassNotFoundException If class of
583      * a serialized object cannot be found.
584      * @since     JDK1.1
585      */
586     protected Class resolveClass(ObjectStreamClass v)
587   throws IOException, ClassNotFoundException {
588         if (loader != null) {
589             return loader.loadClass(v.getName());
590         } else {
591             return super.resolveClass(v);
592         }
593     }
594 
595 }