KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > outerj > daisy > blobstore > impl > FSBlobStore


1 /*
2  * Copyright 2004 Outerthought bvba and Schaubroeck nv
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.outerj.daisy.blobstore.impl;
17
18 import org.outerj.daisy.blobstore.BlobStore;
19 import org.outerj.daisy.blobstore.NonExistingBlobException;
20 import org.outerj.daisy.blobstore.BlobIOException;
21 import org.outerj.daisy.configutil.PropertyResolver;
22 import org.apache.avalon.framework.configuration.Configurable;
23 import org.apache.avalon.framework.configuration.Configuration;
24 import org.apache.avalon.framework.configuration.ConfigurationException;
25 import org.apache.avalon.framework.activity.Initializable;
26
27 import java.io.*;
28 import java.security.SecureRandom JavaDoc;
29
30 import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
31 import EDU.oswego.cs.dl.util.concurrent.Sync;
32
33 /**
34  * Implementation of {@link BlobStore} that stores blobs in a directory
35  * on the filesystem.
36  *
37  * <p>This class in an Avalon component, and hence its lifecycle interfaces
38  * should be respected before being used.
39  *
40  * @avalon.component version="1.0" name="blobstore" lifestyle="singleton"
41  * @avalon.service type="org.outerj.daisy.blobstore.BlobStore"
42  */

43 public class FSBlobStore implements BlobStore, Configurable, Initializable {
44     private static final int DIRECTORY_DEPTH = 4;
45     private static final int DIRECTORY_NAME_LENGTH = 2;
46     private File directory;
47     private SecureRandom JavaDoc random = null;
48     private int KEYLENGTH = 20;
49     private WriterPreferenceReadWriteLock suspendWritesLock = new WriterPreferenceReadWriteLock();
50
51     public void configure(Configuration configuration) throws ConfigurationException {
52         String JavaDoc directoryName = PropertyResolver.resolveProperties(configuration.getChild("directory").getValue());
53         directory = new File(directoryName);
54         if (!directory.exists())
55             throw new ConfigurationException("The specified directory does not exist: " + directoryName);
56         if (!directory.isDirectory())
57             throw new ConfigurationException("The specified directory is not a directory: " + directoryName);
58     }
59
60     public void initialize() throws Exception JavaDoc {
61         try {
62             random = SecureRandom.getInstance("SHA1PRNG");
63         }
64         catch(java.security.NoSuchAlgorithmException JavaDoc nsae) {
65             // maybe we are on IBM's SDK
66
random = SecureRandom.getInstance("IBMSecureRandom");
67         }
68         random.setSeed(System.currentTimeMillis());
69     }
70
71     private void checkSuspendWrites() {
72         boolean hasSuspendLock = false;
73         try {
74             hasSuspendLock = suspendWritesLock.readLock().attempt(0);
75         } catch (InterruptedException JavaDoc e) {
76             // ignore
77
}
78         if (!hasSuspendLock)
79             throw new RuntimeException JavaDoc("Write access to the blobstore is currently disabled. Try again later.");
80     }
81
82     public void delete(String JavaDoc name) throws NonExistingBlobException {
83         checkSuspendWrites();
84         try {
85             File file = nameToFile(name);
86             if (!file.exists())
87                 throw new NonExistingBlobException(name);
88
89             file.delete();
90         } finally {
91             suspendWritesLock.readLock().release();
92         }
93     }
94
95     public String JavaDoc store(byte[] data) throws BlobIOException {
96         File file = null;
97         FileOutputStream fos = null;
98         checkSuspendWrites();
99         try {
100             try {
101                 file = createFile();
102                 fos = new FileOutputStream(file);
103                 fos.write(data);
104                 fos.getFD().sync();
105             } finally {
106                 if (fos != null)
107                     fos.close();
108             }
109         } catch (IOException e) {
110             throw new BlobIOException("Error storing blob.", e);
111         } finally {
112             suspendWritesLock.readLock().release();
113         }
114         return fileToName(file);
115     }
116
117     private static final int BUFFER_SIZE = 32768;
118
119     public String JavaDoc store(InputStream is) throws BlobIOException {
120         checkSuspendWrites();
121         try {
122             File file = null;
123             FileOutputStream fos = null;
124             byte[] buffer = new byte[BUFFER_SIZE];
125             try {
126                 try {
127                     file = createFile();
128                     fos = new FileOutputStream(file);
129                     BufferedOutputStream bos = new BufferedOutputStream(fos);
130                     int read;
131                     while ((read = is.read(buffer)) != -1) {
132                         bos.write(buffer, 0, read);
133                     }
134                     bos.flush();
135                     fos.getFD().sync();
136                 } finally {
137                     if (fos != null)
138                         fos.close();
139                 }
140             } catch (IOException e) {
141                 throw new BlobIOException("Error storing blob.", e);
142             }
143             return fileToName(file);
144         } finally {
145             suspendWritesLock.readLock().release();
146             try {
147                 is.close();
148             } catch (IOException e) {
149                 throw new BlobIOException("Error closing input stream.", e);
150             }
151         }
152     }
153
154     public InputStream retrieve(String JavaDoc name) throws BlobIOException, NonExistingBlobException {
155         File file = nameToFile(name);
156         if (!file.exists())
157             throw new NonExistingBlobException(name);
158
159         try {
160             return new FileInputStream(file);
161         } catch (FileNotFoundException e) {
162             throw new BlobIOException("Error retrieving the blob named \"" + name + "\".", e);
163         }
164     }
165
166     /**
167      * Generates a filename and creates the file.
168      *
169      * <p>The implementation uses a secure random number generator, which is strictly
170      * speaking not necessary, but it won't hurt either. This code is an adjustment of
171      * the code that generates Web Continuation ID's in Cocoon.
172      */

173     private File createFile() throws IOException {
174         byte[] bytes = new byte[KEYLENGTH];
175         char[] result = new char[KEYLENGTH * 2];
176
177         while (true) {
178             random.nextBytes(bytes);
179
180             for (int i = 0; i < KEYLENGTH; i++) {
181                 byte ch = bytes[i];
182                 result[2 * i] = Character.forDigit(Math.abs(ch >> 4), 16);
183                 result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f), 16);
184             }
185
186             String JavaDoc id = new String JavaDoc(result);
187             synchronized (this) {
188                 File file = nameToFile(id);
189                 if (file.createNewFile()) {
190                     return file;
191                 }
192             }
193         }
194     }
195
196     public boolean suspendWrites(long msecs) throws InterruptedException JavaDoc {
197         return suspendWritesLock.writeLock().attempt(msecs);
198     }
199
200     public void resumeWrites() {
201         suspendWritesLock.writeLock().release();
202     }
203
204     public Sync getAvoidSuspendLock() {
205         return suspendWritesLock.readLock();
206     }
207     
208     private File nameToFile(String JavaDoc name) {
209         StringBuffer JavaDoc subdirName = new StringBuffer JavaDoc((DIRECTORY_NAME_LENGTH+1) * DIRECTORY_DEPTH);
210         int position = 0;
211         for (int i = 0; i < DIRECTORY_DEPTH; i++)
212             subdirName.append(name.substring(position, position+=DIRECTORY_NAME_LENGTH)).append(File.separator);
213         
214         File subdir = new File(directory, subdirName.toString());
215         subdir.mkdirs();
216                    
217         return new File(subdir, name.substring(position));
218     }
219     
220     private String JavaDoc fileToName(final File file) throws BlobIOException{
221         File workFile = new File(file, "");
222         String JavaDoc name = file.getName();
223         for (int i = 0; i < DIRECTORY_DEPTH; i++) {
224             workFile = workFile.getParentFile();
225             name = workFile.getName() + name;
226         }
227         
228         if (name.length() != KEYLENGTH*2)
229             throw new BlobIOException("Blob '" + file.getPath() + "' does not have the correct id '" + name + "'");
230             
231         return name;
232     }
233 }
234
Popular Tags