1 16 package org.apache.cocoon.components.store; 17 18 import org.apache.avalon.framework.CascadingRuntimeException; 19 import org.apache.avalon.framework.context.Context; 20 import org.apache.avalon.framework.context.ContextException; 21 import org.apache.avalon.framework.context.Contextualizable; 22 import org.apache.avalon.framework.logger.AbstractLogEnabled; 23 import org.apache.avalon.framework.thread.ThreadSafe; 24 import org.apache.avalon.framework.parameters.Parameterizable; 25 import org.apache.avalon.framework.parameters.Parameters; 26 import org.apache.avalon.framework.parameters.ParameterException; 27 import org.apache.cocoon.Constants; 28 import org.apache.cocoon.util.IOUtils; 29 import java.io.ByteArrayOutputStream ; 30 import java.io.File ; 31 import java.io.IOException ; 32 import java.io.OutputStreamWriter ; 33 import java.util.BitSet ; 34 import java.util.Enumeration ; 35 36 46 public final class FilesystemStore extends AbstractLogEnabled 47 implements Store, Contextualizable, Parameterizable, ThreadSafe { 48 49 protected File workDir; 50 protected File cacheDir; 51 52 53 protected File directoryFile; 54 protected volatile String directoryPath; 55 56 59 public void setDirectory(final String directory) 60 throws IOException { 61 this.setDirectory(new File (directory)); 62 } 63 64 public void contextualize(final Context context) 65 throws ContextException { 66 this.workDir = (File )context.get(Constants.CONTEXT_WORK_DIR); 67 this.cacheDir = (File )context.get(Constants.CONTEXT_CACHE_DIR); 68 } 69 70 public void parameterize(Parameters params) 71 throws ParameterException { 72 try { 73 if (params.getParameterAsBoolean("use-cache-directory", false)) { 74 if (this.getLogger().isDebugEnabled()) 75 getLogger().debug("Using cache directory: " + cacheDir); 76 setDirectory(cacheDir); 77 } else if (params.getParameterAsBoolean("use-work-directory", false)) { 78 if (this.getLogger().isDebugEnabled()) 79 getLogger().debug("Using work directory: " + workDir); 80 setDirectory(workDir); 81 } else if (params.getParameter("directory", null) != null) { 82 String dir = params.getParameter("directory"); 83 dir = IOUtils.getContextFilePath(workDir.getPath(), dir); 84 if (this.getLogger().isDebugEnabled()) 85 getLogger().debug("Using directory: " + dir); 86 setDirectory(new File (dir)); 87 } else { 88 try { 89 setDirectory(workDir); 91 } catch (IOException e) { 92 } 94 } 95 } catch (IOException e) { 96 throw new ParameterException("Unable to set directory", e); 97 } 98 } 99 100 103 public void setDirectory(final File directory) 104 throws IOException { 105 this.directoryFile = directory; 106 107 108 this.directoryPath = IOUtils.getFullFilename(this.directoryFile); 109 this.directoryPath += File.separator; 110 111 112 if (!this.directoryFile.exists()) { 113 114 if (!this.directoryFile.mkdir()) { 115 throw new IOException ( 116 "Error creating store directory '" + this.directoryPath + "': "); 117 } 118 } 119 120 121 if (!this.directoryFile.isDirectory()) { 122 throw new IOException ("'" + this.directoryPath + "' is not a directory"); 123 } 124 125 126 if (!(this.directoryFile.canRead() && this.directoryFile.canWrite())) { 127 throw new IOException ( 128 "Directory '" + this.directoryPath + "' is not readable/writable" 129 ); 130 } 131 } 132 133 136 public String getDirectoryPath() { 137 return this.directoryPath; 138 } 139 140 143 public synchronized Object get(final Object key) { 144 final File file = fileFromKey(key); 145 146 if (file != null && file.exists()) { 147 if (this.getLogger().isDebugEnabled()) { 148 getLogger().debug("Found file: " + key); 149 } 150 try { 151 return IOUtils.deserializeObject(file); 152 } catch (Exception any) { 153 getLogger().error("Error during deseralization.", any); 154 } 155 } else { 156 if (this.getLogger().isDebugEnabled()) { 157 getLogger().debug("NOT Found file: " + key); 158 } 159 } 160 161 return null; 162 } 163 164 170 public synchronized void store(final Object key, final Object value) 171 throws IOException { 172 final File file = fileFromKey(key); 173 174 175 final File parent = file.getParentFile(); 176 if (parent != null) { 177 parent.mkdirs(); 178 } 179 180 181 if (value == null) { 182 if (file.exists()) { 183 if (!file.delete()) { 184 getLogger().error("File cannot be deleted: " + file.toString()); 185 return; 186 } 187 } 188 189 file.mkdir(); 190 } else if (value instanceof String ) { 191 192 IOUtils.serializeString(file, (String ) value); 193 } else { 194 195 IOUtils.serializeObject(file, value); 196 } 197 } 198 199 202 public synchronized void hold(final Object key, final Object value) 203 throws IOException { 204 this.store(key, value); 205 final File file = this.fileFromKey(key); 206 if (file != null) { 207 file.deleteOnExit(); 208 } 209 } 210 211 214 public synchronized void remove(final Object key) { 215 final File file = fileFromKey(key); 216 if (file != null) { 217 file.delete(); 218 } 219 } 220 221 224 public synchronized boolean containsKey(final Object key) { 225 final File file = fileFromKey(key); 226 if (file == null) { 227 return false; 228 } 229 return file.exists(); 230 } 231 232 235 public synchronized Enumeration keys() { 236 final FSEnumeration fsEnum = new FSEnumeration(); 237 this.addKeys(fsEnum, this.directoryFile); 238 return fsEnum; 239 } 240 241 245 public synchronized int size() { 246 return countKeys(this.directoryFile); 247 } 248 249 protected void addKeys(FSEnumeration fsEnum, File directory) { 250 final int subStringBegin = this.directoryFile.getAbsolutePath().length() + 1; 251 final File [] files = directory.listFiles(); 252 for (int i=0; i<files.length; i++) { 253 if (files[i].isDirectory()) { 254 this.addKeys(fsEnum, files[i]); 255 } else { 256 fsEnum.add(this.decode(files[i].getAbsolutePath().substring(subStringBegin))); 257 } 258 } 259 } 260 261 protected int countKeys(File directory) { 262 int count = 0; 263 final File [] files = directory.listFiles(); 264 for (int i=0; i<files.length; i++) { 265 if (files[i].isDirectory()) { 266 count += this.countKeys(files[i]); 267 } else { 268 count ++; 269 } 270 } 271 return count; 272 } 273 274 final class FSEnumeration implements Enumeration { 275 private String [] array; 276 private int index; 277 private int length; 278 279 FSEnumeration() { 280 this.array = new String [16]; 281 this.length = 0; 282 this.index = 0; 283 } 284 285 public void add(String key) { 286 if (this.length == array.length) { 287 String [] newarray = new String [this.length + 16]; 288 System.arraycopy(this.array, 0, newarray, 0, this.array.length); 289 this.array = newarray; 290 } 291 this.array[this.length] = key; 292 this.length++; 293 } 294 295 public boolean hasMoreElements() { 296 return (this.index < this.length); 297 } 298 299 public Object nextElement() { 300 if (this.hasMoreElements()) { 301 this.index++; 302 return this.array[index-1]; 303 } 304 return null; 305 } 306 } 307 308 309 protected File fileFromKey(final Object key) { 310 return IOUtils.createFile(this.directoryFile, this.encode(key.toString())); 311 } 312 313 public String getString(final Object key) 314 throws IOException { 315 final File file = this.fileFromKey(key); 316 if (file != null) { 317 return IOUtils.deserializeString(file); 318 } 319 320 return null; 321 } 322 323 public synchronized void free() {} 324 325 public synchronized Object getObject(final Object key) 326 throws IOException , ClassNotFoundException 327 { 328 final File file = this.fileFromKey(key); 329 if (file != null) { 330 return IOUtils.deserializeObject(file); 331 } 332 333 return null; 334 } 335 336 342 protected String decode( String filename ) 343 { 344 try { 345 return java.net.URLDecoder.decode( filename ); 346 } catch (Exception local) { 347 throw new CascadingRuntimeException("Exception in decode", local); 348 } 349 } 350 351 352 static BitSet charactersDontNeedingEncoding; 353 static final int characterCaseDiff = ('a' - 'A'); 354 355 356 static 357 { 358 charactersDontNeedingEncoding = new BitSet (256); 359 int i; 360 for (i = 'a'; i <= 'z'; i++) 361 { 362 charactersDontNeedingEncoding.set(i); 363 } 364 for (i = 'A'; i <= 'Z'; i++) 365 { 366 charactersDontNeedingEncoding.set(i); 367 } 368 for (i = '0'; i <= '9'; i++) 369 { 370 charactersDontNeedingEncoding.set(i); 371 } 372 charactersDontNeedingEncoding.set('-'); 373 charactersDontNeedingEncoding.set('_'); 374 charactersDontNeedingEncoding.set('('); 375 charactersDontNeedingEncoding.set(')'); 376 } 377 378 386 public String encode(String s) { 387 final StringBuffer out = new StringBuffer ( s.length() ); 388 final ByteArrayOutputStream buf = new ByteArrayOutputStream ( 32 ); 389 final OutputStreamWriter writer = new OutputStreamWriter ( buf ); 390 for (int i = 0; i < s.length(); i++) 391 { 392 int c = s.charAt(i); 393 if (charactersDontNeedingEncoding.get(c)) 394 { 395 out.append((char)c); 396 } 397 else 398 { 399 try 400 { 401 writer.write(c); 402 writer.flush(); 403 } 404 catch(IOException e) 405 { 406 buf.reset(); 407 continue; 408 } 409 byte[] ba = buf.toByteArray(); 410 for (int j = 0; j < ba.length; j++) 411 { 412 out.append('%'); 413 char ch = Character.forDigit((ba[j] >> 4) & 0xF, 16); 414 if (Character.isLetter(ch)) 417 { 418 ch -= characterCaseDiff; 419 } 420 out.append(ch); 421 ch = Character.forDigit(ba[j] & 0xF, 16); 422 if (Character.isLetter(ch)) 423 { 424 ch -= characterCaseDiff; 425 } 426 out.append(ch); 427 } 428 buf.reset(); 429 } 430 } 431 432 return out.toString(); 433 } 434 } 435 | Popular Tags |