1 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 ; 29 30 import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock; 31 import EDU.oswego.cs.dl.util.concurrent.Sync; 32 33 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 random = null; 48 private int KEYLENGTH = 20; 49 private WriterPreferenceReadWriteLock suspendWritesLock = new WriterPreferenceReadWriteLock(); 50 51 public void configure(Configuration configuration) throws ConfigurationException { 52 String 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 { 61 try { 62 random = SecureRandom.getInstance("SHA1PRNG"); 63 } 64 catch(java.security.NoSuchAlgorithmException nsae) { 65 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 e) { 76 } 78 if (!hasSuspendLock) 79 throw new RuntimeException ("Write access to the blobstore is currently disabled. Try again later."); 80 } 81 82 public void delete(String 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 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 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 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 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 id = new String (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 { 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 name) { 209 StringBuffer subdirName = new StringBuffer ((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 fileToName(final File file) throws BlobIOException{ 221 File workFile = new File(file, ""); 222 String 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 |