Author: toad
Date: 2007-07-21 01:42:16 +0000 (Sat, 21 Jul 2007)
New Revision: 14222

Added:
   trunk/freenet/src/freenet/support/io/BaseFileBucket.java
   trunk/freenet/src/freenet/support/io/PersistentTempFileBucket.java
Removed:
   trunk/freenet/src/freenet/support/io/SpyInputStream.java
   trunk/freenet/src/freenet/support/io/SpyOutputStream.java
   trunk/freenet/src/freenet/support/io/TempBucketHook.java
Modified:
   trunk/freenet/src/freenet/client/ArchiveManager.java
   trunk/freenet/src/freenet/client/TempStoreElement.java
   trunk/freenet/src/freenet/support/io/FileBucket.java
   trunk/freenet/src/freenet/support/io/FilenameGenerator.java
   trunk/freenet/src/freenet/support/io/NullPersistentFileTracker.java
   trunk/freenet/src/freenet/support/io/PersistentFileTracker.java
   trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
   trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
   trunk/freenet/src/freenet/support/io/TempBucketFactory.java
   trunk/freenet/src/freenet/support/io/TempFileBucket.java
Log:
Untested significant refactoring of FileBucket/TempFileBucket.
Will save some memory, also code simplifications.

Modified: trunk/freenet/src/freenet/client/ArchiveManager.java
===================================================================
--- trunk/freenet/src/freenet/client/ArchiveManager.java        2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/client/ArchiveManager.java        2007-07-21 
01:42:16 UTC (rev 14222)
@@ -22,6 +22,7 @@
 import freenet.support.io.FileBucket;
 import freenet.support.io.FilenameGenerator;
 import freenet.support.io.PaddedEphemerallyEncryptedBucket;
+import freenet.support.io.TempFileBucket;

 /**
  * Cache of recently decoded archives:
@@ -441,8 +442,9 @@
         * go over the maximum size. Will obviously keep its key when we move 
it to main.
         */
        private TempStoreElement makeTempStoreBucket(long size) {
-               File myFile = filenameGenerator.makeRandomFilename();
-               FileBucket fb = new FileBucket(myFile, false, false, true, 
true, true);
+               long id = filenameGenerator.makeRandomFilename();
+               File myFile = filenameGenerator.getFilename(id);
+               TempFileBucket fb = new TempFileBucket(id, filenameGenerator);

                byte[] cipherKey = new byte[32];
                random.nextBytes(cipherKey);

Modified: trunk/freenet/src/freenet/client/TempStoreElement.java
===================================================================
--- trunk/freenet/src/freenet/client/TempStoreElement.java      2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/client/TempStoreElement.java      2007-07-21 
01:42:16 UTC (rev 14222)
@@ -5,15 +5,15 @@

 import java.io.File;

-import freenet.support.io.FileBucket;
 import freenet.support.io.PaddedEphemerallyEncryptedBucket;
+import freenet.support.io.TempFileBucket;

 class TempStoreElement {
        final File myFilename;
        final PaddedEphemerallyEncryptedBucket bucket;
-       final FileBucket underBucket;
+       final TempFileBucket underBucket;

-       TempStoreElement(File myFile, FileBucket fb, 
PaddedEphemerallyEncryptedBucket encryptedBucket) {
+       TempStoreElement(File myFile, TempFileBucket fb, 
PaddedEphemerallyEncryptedBucket encryptedBucket) {
                this.myFilename = myFile;
                this.underBucket = fb;
                this.bucket = encryptedBucket;

Added: trunk/freenet/src/freenet/support/io/BaseFileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/BaseFileBucket.java                    
        (rev 0)
+++ trunk/freenet/src/freenet/support/io/BaseFileBucket.java    2007-07-21 
01:42:16 UTC (rev 14222)
@@ -0,0 +1,372 @@
+package freenet.support.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+public abstract class BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {
+
+       protected long length;
+       // JVM caches File.size() and there is no way to flush the cache, so we
+       // need to track it ourselves
+       protected long fileRestartCounter;
+
+       protected static String tempDir = null;
+
+       public BaseFileBucket(File file) {
+               this.length = file.length();
+               if(deleteOnExit()) {
+                       try {
+                               file.deleteOnExit();
+                       } catch (NullPointerException e) {
+                               Logger.error(this, "Impossible: "+e, e);
+                               System.err.println("Impossible: "+e);
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public OutputStream getOutputStream() throws IOException {
+               synchronized (this) {
+                       File file = getFile();
+                       if(isReadOnly())
+                               throw new IOException("Bucket is read-only");
+                       
+                       if(createFileOnly() && file.exists())
+                               throw new FileExistsException(file);
+                       
+                       // FIXME: behaviour depends on UNIX semantics, to 
totally abstract
+                       // it out we would have to kill the old write streams 
here
+                       // FIXME: what about existing streams? Will ones on 
append append
+                       // to the new truncated file? Do we want them to? What 
about
+                       // truncated ones? We should kill old streams here, 
right?
+                       return newFileBucketOutputStream(createFileOnly() ? 
getTempfile() : file, file.getPath(), ++fileRestartCounter);
+               }
+       }
+
+       protected abstract boolean createFileOnly();
+       
+       protected abstract boolean deleteOnExit();
+       
+       protected abstract boolean deleteOnFinalize();
+       
+       protected abstract boolean deleteOnFree();
+
+       /**
+        * Create a temporary file in the same directory as this file.
+        */
+       protected File getTempfile() throws IOException {
+               File file = getFile();
+               File f = File.createTempFile(file.getName(), ".freenet-tmp", 
file.getParentFile());
+               if(deleteOnExit()) f.deleteOnExit();
+               return f;
+       }
+       
+       protected FileBucketOutputStream newFileBucketOutputStream(
+               File tempfile, String s, long streamNumber) throws IOException {
+               return new FileBucketOutputStream(tempfile, s, streamNumber);
+       }
+
+       protected synchronized void resetLength() {
+               length = 0;
+       }
+
+       /**
+        * Internal OutputStream impl.
+        * If createFileOnly is set, we won't overwrite an existing file, and 
we write to a temp file
+        * then rename over the target. Note that we can't use createNewFile 
then new FOS() because while
+        * createNewFile is atomic, the combination is not, so if we do it we 
are vulnerable to symlink
+        * attacks.
+        * @author toad
+        */
+       class FileBucketOutputStream extends FileOutputStream {
+
+               private long restartCount;
+               private File tempfile;
+               private boolean closed;
+               
+               protected FileBucketOutputStream(
+                       File tempfile, String s, long restartCount)
+                       throws FileNotFoundException {
+                       super(tempfile, false);
+                       if(Logger.shouldLog(Logger.MINOR, this))
+                               Logger.minor(this, "Writing to "+tempfile+" for 
"+getFile());
+                       this.tempfile = tempfile;
+                       resetLength();
+                       this.restartCount = restartCount;
+                       closed = false;
+               }
+               
+               protected void confirmWriteSynchronized() throws IOException {
+                       if (fileRestartCounter > restartCount)
+                               throw new IllegalStateException("writing to 
file after restart");
+                       if(isReadOnly())
+                               throw new IOException("File is read-only");
+               }
+               
+               public void write(byte[] b) throws IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b);
+                               length += b.length;
+                       }
+               }
+
+               public void write(byte[] b, int off, int len) throws 
IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b, off, len);
+                               length += len;
+                       }
+               }
+
+               public void write(int b) throws IOException {
+                       synchronized (BaseFileBucket.this) {
+                               confirmWriteSynchronized();
+                               super.write(b);
+                               length++;
+                       }
+               }
+               
+               public void close() throws IOException {
+                       File file;
+                       synchronized(this) {
+                               if(closed) return;
+                               closed = true;
+                               file = getFile();
+                       }
+                       boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
+                       if(logMINOR)
+                               Logger.minor(this, "Closing 
"+BaseFileBucket.this);
+                       try {
+                               super.close();
+                       } catch (IOException e) {
+                               if(logMINOR)
+                                       Logger.minor(this, "Failed closing 
"+BaseFileBucket.this+" : "+e, e);
+                               if(createFileOnly()) tempfile.delete();
+                               throw e;
+                       }
+                       if(createFileOnly()) {
+                               if(file.exists()) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "File exists 
creating file for "+this);
+                                       tempfile.delete();
+                                       throw new FileExistsException(file);
+                               }
+                               if(!tempfile.renameTo(file)) {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Cannot 
rename file for "+this);
+                                       if(file.exists()) throw new 
FileExistsException(file);
+                                       tempfile.delete();
+                                       if(logMINOR)
+                                               Logger.minor(this, "Deleted, 
cannot rename file for "+this);
+                                       throw new IOException("Cannot rename 
file");
+                               }
+                       }
+               }
+       }
+
+       class FileBucketInputStream extends FileInputStream {
+               Exception e;
+
+               public FileBucketInputStream(File f) throws IOException {
+                       super(f);
+                       if (Logger.shouldLog(Logger.DEBUG, this))
+                               e = new Exception("debug");
+               }
+       }
+
+       public synchronized InputStream getInputStream() throws IOException {
+               File file = getFile();
+               if(!file.exists()) {
+                       Logger.normal(this, "File does not exist: "+file+" for 
"+this);
+                       return new NullInputStream();
+               } else 
+                       return new FileBucketInputStream(file);
+       }
+
+       /**
+        * @return the name of the file.
+        */
+       public synchronized String getName() {
+               return getFile().getName();
+       }
+
+       public synchronized long size() {
+               return length;
+       }
+
+       /**
+        * Actually delete the underlying file. Called by finalizer, will not be
+        * called twice. But length must still be valid when calling it.
+        */
+       protected synchronized void deleteFile() {
+               getFile().delete();
+       }
+
+       public void finalize() {
+               if(deleteOnFinalize())
+                       free(true);
+       }
+
+       /**
+        * Return directory used for temp files.
+        */
+       public final synchronized static String getTempDir() {
+               return tempDir;  // **FIXME**/TODO: locking on tempDir needs to 
be checked by a Java guru for consistency
+       }
+
+       /**
+        * Set temp file directory.
+        * <p>
+        * The directory must exist.
+        */
+       public final synchronized static void setTempDir(String dirName) {
+               File dir = new File(dirName);
+               if (!(dir.exists() && dir.isDirectory() && dir.canWrite())) {
+                       throw new IllegalArgumentException(
+                               "Bad Temp Directory: " + dir.getAbsolutePath());
+               }
+               tempDir = dirName;  // **FIXME**/TODO: locking on tempDir needs 
to be checked by a Java guru for consistency
+       }
+
+       // determine the temp directory in one of several ways
+
+       static {
+               // Try the Java property (1.2 and above)
+               tempDir = System.getProperty("java.io.tmpdir");
+
+               // Deprecated calls removed.
+
+               // Try TEMP and TMP
+               //      if (tempDir == null) {
+               //          tempDir = System.getenv("TEMP");
+               //      }
+
+               //      if (tempDir == null) {
+               //          tempDir = System.getenv("TMP");
+               //      }
+
+               // make some semi-educated guesses based on OS.
+
+               if (tempDir == null) {
+                       String os = System.getProperty("os.name");
+                       if (os != null) {
+
+                               String[] candidates = null;
+
+                               // XXX: Add more possible OSes here.
+                               if (os.equalsIgnoreCase("Linux")
+                                       || os.equalsIgnoreCase("FreeBSD")) {
+                                       String[] linuxCandidates = { "/tmp", 
"/var/tmp" };
+                                       candidates = linuxCandidates;
+                               } else if (os.equalsIgnoreCase("Windows")) {
+                                       String[] windowsCandidates =
+                                               { "C:\\TEMP", 
"C:\\WINDOWS\\TEMP" };
+                                       candidates = windowsCandidates;
+                               }
+
+                               if (candidates != null) {
+                                       for (int i = 0; i < candidates.length; 
i++) {
+                                               File path = new 
File(candidates[i]);
+                                               if (path.exists()
+                                                       && path.isDirectory()
+                                                       && path.canWrite()) {
+                                                       tempDir = candidates[i];
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+
+               // last resort -- use current working directory
+
+               if (tempDir == null) {
+                       // This can be null -- but that's OK, null => cwd for 
File
+                       // constructor, anyways.
+                       tempDir = System.getProperty("user.dir");
+               }
+       }
+
+       public synchronized Bucket[] split(int splitSize) {
+               if(length > ((long)Integer.MAX_VALUE) * splitSize)
+                       throw new IllegalArgumentException("Way too big!: 
"+length+" for "+splitSize);
+               int bucketCount = (int) (length / splitSize);
+               if(length % splitSize > 0) bucketCount++;
+               Bucket[] buckets = new Bucket[bucketCount];
+               File file = getFile();
+               for(int i=0;i<buckets.length;i++) {
+                       long startAt = i * splitSize * 1L;
+                       long endAt = Math.min(startAt + splitSize * 1L, length);
+                       long len = endAt - startAt;
+                       buckets[i] = new ReadOnlyFileSliceBucket(file, startAt, 
len);
+               }
+               return buckets;
+       }
+
+       public void free() {
+               free(false);
+       }
+       
+       public synchronized void free(boolean forceFree) {
+               File file = getFile();
+               if ((deleteOnFree() || forceFree) && file.exists()) {
+                       Logger.debug(this,
+                               "Deleting bucket " + file.getName());
+                       deleteFile();
+                       if (file.exists())
+                               Logger.error(this,
+                                       "Delete failed on bucket " + 
file.getName());
+               }
+       }
+       
+       public synchronized String toString() {
+               return super.toString()+ ':' +getFile().getPath();
+       }
+
+       /**
+        * Returns the file object this buckets data is kept in.
+        */
+       public abstract File getFile();
+       
+       public synchronized SimpleFieldSet toFieldSet() {
+               if(deleteOnFinalize()) return null;
+               SimpleFieldSet fs = new SimpleFieldSet(false);
+               fs.putSingle("Type", "FileBucket");
+               fs.putSingle("Filename", getFile().toString());
+               fs.put("Length", size());
+               return fs;
+       }
+
+       public static Bucket create(SimpleFieldSet fs, PersistentFileTracker f) 
throws CannotCreateFromFieldSetException {
+               String tmp = fs.get("Filename");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
+               File file = FileUtil.getCanonicalFile(new File(tmp));
+               if(f.matches(file)) {
+                       return PersistentTempFileBucket.create(fs, f);
+               }
+               tmp = fs.get("Length");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
+               try {
+                       long length = Long.parseLong(tmp);
+                       if(length !=  file.length())
+                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
+               } catch (NumberFormatException e) {
+                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
+               }
+               FileBucket bucket = new FileBucket(file, false, true, false, 
false, false);
+               if(file.exists()) // no point otherwise!
+                       f.register(file);
+               return bucket;
+       }
+
+}

Modified: trunk/freenet/src/freenet/support/io/FileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/FileBucket.java        2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/FileBucket.java        2007-07-21 
01:42:16 UTC (rev 14222)
@@ -4,16 +4,7 @@
 package freenet.support.io;

 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;

-import freenet.crypt.RandomSource;
-import freenet.support.Logger;
-import freenet.support.SimpleFieldSet;
 import freenet.support.api.Bucket;

 /**
@@ -21,7 +12,7 @@
  * 
  * @author oskar
  */
-public class FileBucket implements Bucket, SerializableToFieldSetBucket {
+public class FileBucket extends BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {

        protected final File file;
        protected boolean readOnly;
@@ -29,7 +20,6 @@
        protected boolean deleteOnFree;
        protected final boolean deleteOnExit;
        protected final boolean createFileOnly;
-       protected long length;
        // JVM caches File.size() and there is no way to flush the cache, so we
        // need to track it ourselves
        protected long fileRestartCounter;
@@ -52,6 +42,7 @@
         * @param deleteOnExit If true, delete the file on a clean exit of the 
JVM. Irreversible - use with care!
         */
        public FileBucket(File file, boolean readOnly, boolean createFileOnly, 
boolean deleteOnFinalize, boolean deleteOnExit, boolean deleteOnFree) {
+               super(file);
                if(file == null) throw new NullPointerException();
                file = file.getAbsoluteFile();
                this.readOnly = readOnly;
@@ -60,323 +51,22 @@
                this.deleteOnFinalize = deleteOnFinalize;
                this.deleteOnFree = deleteOnFree;
                this.deleteOnExit = deleteOnExit;
-               if(deleteOnExit) {
-                       try {
-                               file.deleteOnExit();
-                       } catch (NullPointerException e) {
-                               Logger.error(this, "Impossible: "+e, e);
-                               System.err.println("Impossible: "+e);
-                               e.printStackTrace();
-                       }
-               }
                // Useful for finding temp file leaks.
                // System.err.println("-- FileBucket.ctr(0) -- " +
                // file.getAbsolutePath());
                // (new Exception("get stack")).printStackTrace();
                fileRestartCounter = 0;
-               if(file.exists()) {
-                       length = file.length();
-                       if(!file.canWrite())
-                               readOnly = true;
-               }
-               else length = 0;
+               if(!file.canRead())
+                       this.readOnly = true;
        }

        /**
-        * Creates a new FileBucket in a random temporary file in the temporary
-        * directory.
-        * @throws IOException 
-        */
-       public FileBucket(RandomSource random) throws IOException {
-               // **FIXME**/TODO: locking on tempDir needs to be checked by a 
Java guru for consistency
-               file = File.createTempFile(tempDir, ".freenet.tmp");
-               createFileOnly = true;
-               // Useful for finding temp file leaks.
-               //System.err.println("-- FileBucket.ctr(1) -- " +
-               // file.getAbsolutePath());
-               //(new Exception("get stack")).printStackTrace();
-               deleteOnFinalize = true;
-               deleteOnExit = true;
-               length = 0;
-               file.deleteOnExit();
-       }
-
-       public FileBucket(SimpleFieldSet fs, PersistentFileTracker f) throws 
CannotCreateFromFieldSetException {
-               createFileOnly = true;
-               deleteOnExit = false;
-               String tmp = fs.get("Filename");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
-               this.file = FileUtil.getCanonicalFile(new File(tmp));
-               tmp = fs.get("Length");
-               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
-               try {
-                       length = Long.parseLong(tmp);
-                       if(length !=  file.length())
-                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
-               } catch (NumberFormatException e) {
-                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
-               }
-               if(file.exists()) // no point otherwise!
-                       f.register(file);
-       }
-
-       public OutputStream getOutputStream() throws IOException {
-               synchronized (this) {
-                       if(readOnly)
-                               throw new IOException("Bucket is read-only");
-                       
-                       if(createFileOnly && file.exists())
-                               throw new FileExistsException(file);
-                       
-                       // FIXME: behaviour depends on UNIX semantics, to 
totally abstract
-                       // it out we would have to kill the old write streams 
here
-                       // FIXME: what about existing streams? Will ones on 
append append
-                       // to the new truncated file? Do we want them to? What 
about
-                       // truncated ones? We should kill old streams here, 
right?
-                       return newFileBucketOutputStream(createFileOnly ? 
getTempfile() : file, file.getPath(), ++fileRestartCounter);
-               }
-       }
-
-       /**
-        * Create a temporary file in the same directory as this file.
-        */
-       protected File getTempfile() throws IOException {
-               File f = File.createTempFile(file.getName(), ".freenet-tmp", 
file.getParentFile());
-               if(deleteOnExit) f.deleteOnExit();
-               return f;
-       }
-       
-       protected FileBucketOutputStream newFileBucketOutputStream(
-               File tempfile, String s, long streamNumber) throws IOException {
-               return new FileBucketOutputStream(tempfile, s, streamNumber);
-       }
-
-       protected synchronized void resetLength() {
-               length = 0;
-       }
-
-       /**
-        * Internal OutputStream impl.
-        * If createFileOnly is set, we won't overwrite an existing file, and 
we write to a temp file
-        * then rename over the target. Note that we can't use createNewFile 
then new FOS() because while
-        * createNewFile is atomic, the combination is not, so if we do it we 
are vulnerable to symlink
-        * attacks.
-        * @author toad
-        */
-       class FileBucketOutputStream extends FileOutputStream {
-
-               private long restartCount;
-               private File tempfile;
-               private boolean closed;
-               
-               protected FileBucketOutputStream(
-                       File tempfile, String s, long restartCount)
-                       throws FileNotFoundException {
-                       super(tempfile, false);
-                       if(Logger.shouldLog(Logger.MINOR, this))
-                               Logger.minor(this, "Writing to "+tempfile+" for 
"+file);
-                       this.tempfile = tempfile;
-                       resetLength();
-                       this.restartCount = restartCount;
-                       closed = false;
-               }
-               
-               protected void confirmWriteSynchronized() throws IOException {
-                       if (fileRestartCounter > restartCount)
-                               throw new IllegalStateException("writing to 
file after restart");
-                       if(readOnly)
-                               throw new IOException("File is read-only");
-               }
-               
-               public void write(byte[] b) throws IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b);
-                               length += b.length;
-                       }
-               }
-
-               public void write(byte[] b, int off, int len) throws 
IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b, off, len);
-                               length += len;
-                       }
-               }
-
-               public void write(int b) throws IOException {
-                       synchronized (FileBucket.this) {
-                               confirmWriteSynchronized();
-                               super.write(b);
-                               length++;
-                       }
-               }
-               
-               public void close() throws IOException {
-                       synchronized(this) {
-                               if(closed) return;
-                               closed = true;
-                       }
-                       boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);
-                       if(logMINOR)
-                               Logger.minor(this, "Closing "+FileBucket.this);
-                       try {
-                               super.close();
-                       } catch (IOException e) {
-                               if(logMINOR)
-                                       Logger.minor(this, "Failed closing 
"+FileBucket.this+" : "+e, e);
-                               if(createFileOnly) tempfile.delete();
-                               throw e;
-                       }
-                       if(createFileOnly) {
-                               if(file.exists()) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "File exists 
creating file for "+this);
-                                       tempfile.delete();
-                                       throw new FileExistsException(file);
-                               }
-                               if(!tempfile.renameTo(file)) {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Cannot 
rename file for "+this);
-                                       if(file.exists()) throw new 
FileExistsException(file);
-                                       tempfile.delete();
-                                       if(logMINOR)
-                                               Logger.minor(this, "Deleted, 
cannot rename file for "+this);
-                                       throw new IOException("Cannot rename 
file");
-                               }
-                       }
-               }
-       }
-
-       class FileBucketInputStream extends FileInputStream {
-               Exception e;
-
-               public FileBucketInputStream(File f) throws IOException {
-                       super(f);
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               e = new Exception("debug");
-               }
-       }
-
-       public synchronized InputStream getInputStream() throws IOException {
-               if(!file.exists()) {
-                       Logger.normal(this, "File does not exist: "+file+" for 
"+this);
-                       return new NullInputStream();
-               } else 
-                       return new FileBucketInputStream(file);
-       }
-
-       /**
-        * @return the name of the file.
-        */
-       public synchronized String getName() {
-               return file.getName();
-       }
-
-       public synchronized long size() {
-               return length;
-       }
-
-       /**
         * Returns the file object this buckets data is kept in.
         */
        public synchronized File getFile() {
                return file;
        }

-       /**
-        * Actually delete the underlying file. Called by finalizer, will not be
-        * called twice. But length must still be valid when calling it.
-        */
-       protected synchronized void deleteFile() {
-               file.delete();
-       }
-
-       public void finalize() {
-               if(deleteOnFinalize)
-                       free(deleteOnFinalize);
-       }
-
-       /**
-        * Return directory used for temp files.
-        */
-       public final synchronized static String getTempDir() {
-               return tempDir;  // **FIXME**/TODO: locking on tempDir needs to 
be checked by a Java guru for consistency
-       }
-
-       /**
-        * Set temp file directory.
-        * <p>
-        * The directory must exist.
-        */
-       public final synchronized static void setTempDir(String dirName) {
-               File dir = new File(dirName);
-               if (!(dir.exists() && dir.isDirectory() && dir.canWrite())) {
-                       throw new IllegalArgumentException(
-                               "Bad Temp Directory: " + dir.getAbsolutePath());
-               }
-               tempDir = dirName;  // **FIXME**/TODO: locking on tempDir needs 
to be checked by a Java guru for consistency
-       }
-
-       // determine the temp directory in one of several ways
-
-       static {
-               // Try the Java property (1.2 and above)
-               tempDir = System.getProperty("java.io.tmpdir");
-
-               // Deprecated calls removed.
-
-               // Try TEMP and TMP
-               //      if (tempDir == null) {
-               //          tempDir = System.getenv("TEMP");
-               //      }
-
-               //      if (tempDir == null) {
-               //          tempDir = System.getenv("TMP");
-               //      }
-
-               // make some semi-educated guesses based on OS.
-
-               if (tempDir == null) {
-                       String os = System.getProperty("os.name");
-                       if (os != null) {
-
-                               String[] candidates = null;
-
-                               // XXX: Add more possible OSes here.
-                               if (os.equalsIgnoreCase("Linux")
-                                       || os.equalsIgnoreCase("FreeBSD")) {
-                                       String[] linuxCandidates = { "/tmp", 
"/var/tmp" };
-                                       candidates = linuxCandidates;
-                               } else if (os.equalsIgnoreCase("Windows")) {
-                                       String[] windowsCandidates =
-                                               { "C:\\TEMP", 
"C:\\WINDOWS\\TEMP" };
-                                       candidates = windowsCandidates;
-                               }
-
-                               if (candidates != null) {
-                                       for (int i = 0; i < candidates.length; 
i++) {
-                                               File path = new 
File(candidates[i]);
-                                               if (path.exists()
-                                                       && path.isDirectory()
-                                                       && path.canWrite()) {
-                                                       tempDir = candidates[i];
-                                                       break;
-                                               }
-                                       }
-                               }
-                       }
-               }
-
-               // last resort -- use current working directory
-
-               if (tempDir == null) {
-                       // This can be null -- but that's OK, null => cwd for 
File
-                       // constructor, anyways.
-                       tempDir = System.getProperty("user.dir");
-               }
-       }
-
        public synchronized boolean isReadOnly() {
                return readOnly;
        }
@@ -394,46 +84,19 @@
                deleteOnFinalize = false;
        }

-       public synchronized Bucket[] split(int splitSize) {
-               if(length > ((long)Integer.MAX_VALUE) * splitSize)
-                       throw new IllegalArgumentException("Way too big!: 
"+length+" for "+splitSize);
-               int bucketCount = (int) (length / splitSize);
-               if(length % splitSize > 0) bucketCount++;
-               Bucket[] buckets = new Bucket[bucketCount];
-               for(int i=0;i<buckets.length;i++) {
-                       long startAt = i * splitSize * 1L;
-                       long endAt = Math.min(startAt + splitSize * 1L, length);
-                       long len = endAt - startAt;
-                       buckets[i] = new ReadOnlyFileSliceBucket(file, startAt, 
len);
-               }
-               return buckets;
+       protected boolean createFileOnly() {
+               return createFileOnly;
        }

-       public void free() {
-               free(false);
+       protected boolean deleteOnExit() {
+               return deleteOnExit;
        }
-       
-       public synchronized void free(boolean forceFree) {
-               if ((deleteOnFree || forceFree) && file.exists()) {
-                       Logger.debug(this,
-                               "Deleting bucket " + file.getName());
-                       deleteFile();
-                       if (file.exists())
-                               Logger.error(this,
-                                       "Delete failed on bucket " + 
file.getName());
-               }
+
+       protected boolean deleteOnFinalize() {
+               return deleteOnFinalize;
        }
-       
-       public synchronized String toString() {
-               return super.toString()+ ':' +file.getPath();
-       }

-       public synchronized SimpleFieldSet toFieldSet() {
-               if(deleteOnFinalize) return null;
-               SimpleFieldSet fs = new SimpleFieldSet(false);
-               fs.putSingle("Type", "FileBucket");
-               fs.putSingle("Filename", file.toString());
-               fs.put("Length", length);
-               return fs;
+       protected boolean deleteOnFree() {
+               return deleteOnFree;
        }
 }

Modified: trunk/freenet/src/freenet/support/io/FilenameGenerator.java
===================================================================
--- trunk/freenet/src/freenet/support/io/FilenameGenerator.java 2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/FilenameGenerator.java 2007-07-21 
01:42:16 UTC (rev 14222)
@@ -6,7 +6,6 @@
 import org.tanukisoftware.wrapper.WrapperManager;

 import freenet.crypt.RandomSource;
-import freenet.support.HexUtil;
 import freenet.support.Logger;
 import freenet.support.TimeUtil;

@@ -27,15 +26,14 @@
                this.random = random;
                this.prefix = prefix;
                if (dir == null)
-                       tmpDir = new File(System.getProperty("java.io.tmpdir"));
+                       tmpDir = FileUtil.getCanonicalFile(new 
File(System.getProperty("java.io.tmpdir")));
                else
-                       tmpDir = dir;
+                       tmpDir = FileUtil.getCanonicalFile(dir);
         if(!tmpDir.exists()) {
             tmpDir.mkdir();
                }
                if(!(tmpDir.isDirectory() && tmpDir.canRead() && 
tmpDir.canWrite()))
                        throw new IOException("Not a directory or cannot 
read/write: "+tmpDir);
-               
                if(wipeFiles) {
                        long wipedFiles = 0;
                        long wipeableFiles = 0;
@@ -64,18 +62,38 @@
                }
        }

-       public File makeRandomFilename() {
-               byte[] randomFilename = new byte[8]; // should be plenty
+       public long makeRandomFilename() {
+               long randomFilename; // should be plenty
                while(true) {
-                       random.nextBytes(randomFilename);
-                       String filename = prefix + 
HexUtil.bytesToHex(randomFilename);
+                       randomFilename = random.nextLong();
+                       if(randomFilename == -1) continue; // Disallowed as 
used for error reporting
+                       String filename = prefix + 
Long.toHexString(randomFilename);
                        File ret = new File(tmpDir, filename);
                        if(!ret.exists()) {
                                if(Logger.shouldLog(Logger.MINOR, this))
                                        Logger.minor(this, "Made random 
filename: "+ret, new Exception("debug"));
-                               return ret;
+                               return randomFilename;
                        }
                }
        }

+       public File getFilename(long id) {
+               return new File(tmpDir, prefix + Long.toHexString(id));
+       }
+
+       public boolean matches(File file) {
+               return getID(file) != -1;
+       }
+
+       public long getID(File file) {
+               
if(!(FileUtil.getCanonicalFile(file.getParentFile()).equals(tmpDir))) return -1;
+               String name = file.getName();
+               if(!name.startsWith(prefix)) return -1;
+               try {
+                       return Long.parseLong(name.substring(prefix.length()), 
16);
+               } catch (NumberFormatException e) {
+                       return -1;
+               }
+       }
+
 }

Modified: trunk/freenet/src/freenet/support/io/NullPersistentFileTracker.java
===================================================================
--- trunk/freenet/src/freenet/support/io/NullPersistentFileTracker.java 
2007-07-21 00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/NullPersistentFileTracker.java 
2007-07-21 01:42:16 UTC (rev 14222)
@@ -22,4 +22,16 @@
                return new File(".");
        }

+       public boolean matches(File file) {
+               return false;
+       }
+
+       public FilenameGenerator getGenerator() {
+               return null;
+       }
+
+       public long getID(File file) {
+               return 0;
+       }
+
 }

Modified: trunk/freenet/src/freenet/support/io/PersistentFileTracker.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentFileTracker.java     
2007-07-21 00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/PersistentFileTracker.java     
2007-07-21 01:42:16 UTC (rev 14222)
@@ -21,5 +21,14 @@
         * Get the persistent temp files directory.
         */
        public File getDir();
+
+       /**
+        * Is the file in question one of our persistent temp files?
+        */
+       public boolean matches(File file);
+
+       public FilenameGenerator getGenerator();
+
+       public long getID(File file);

 }

Modified: trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java       
2007-07-21 00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/PersistentTempBucketFactory.java       
2007-07-21 01:42:16 UTC (rev 14222)
@@ -96,7 +96,7 @@
        }

        private Bucket makeRawBucket(long size) throws IOException {
-               return new FileBucket(fg.makeRandomFilename(), false, true, 
false, false, true);
+               return new PersistentTempFileBucket(fg.makeRandomFilename(), 
fg);
        }

        public Bucket makeBucket(long size) throws IOException {
@@ -130,4 +130,16 @@
                return dir;
        }

+       public FilenameGenerator getGenerator() {
+               return fg;
+       }
+
+       public boolean matches(File file) {
+               return fg.matches(file);
+       }
+
+       public long getID(File file) {
+               return fg.getID(file);
+       }
+
 }

Added: trunk/freenet/src/freenet/support/io/PersistentTempFileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/PersistentTempFileBucket.java          
                (rev 0)
+++ trunk/freenet/src/freenet/support/io/PersistentTempFileBucket.java  
2007-07-21 01:42:16 UTC (rev 14222)
@@ -0,0 +1,40 @@
+package freenet.support.io;
+
+import java.io.File;
+
+import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;
+
+public class PersistentTempFileBucket extends TempFileBucket {
+
+       protected PersistentTempFileBucket(long id, FilenameGenerator 
generator) {
+               super(id, generator);
+       }
+
+       protected boolean deleteOnFinalize() {
+               // Do not delete on finalize
+               return false;
+       }
+
+       public static Bucket create(SimpleFieldSet fs, PersistentFileTracker f) 
throws CannotCreateFromFieldSetException {
+               String tmp = fs.get("Filename");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
filename");
+               File file = FileUtil.getCanonicalFile(new File(tmp));
+               long id = f.getID(file);
+               tmp = fs.get("Length");
+               if(tmp == null) throw new CannotCreateFromFieldSetException("No 
length");
+               long length;
+               try {
+                       length = Long.parseLong(tmp);
+                       if(length !=  file.length())
+                               throw new 
CannotCreateFromFieldSetException("Invalid length: should be "+length+" 
actually "+file.length()+" on "+file);
+               } catch (NumberFormatException e) {
+                       throw new CannotCreateFromFieldSetException("Corrupt 
length "+tmp, e);
+               }
+               Bucket bucket = new PersistentTempFileBucket(id, 
f.getGenerator());
+               if(file.exists()) // no point otherwise!
+                       f.register(file);
+               return bucket;
+       }
+       
+}

Modified: 
trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java  
2007-07-21 00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/SerializableToFieldSetBucketUtil.java  
2007-07-21 01:42:16 UTC (rev 14222)
@@ -56,7 +56,7 @@

                        throw new CannotCreateFromFieldSetException("No type");
                } else if(type.equals("FileBucket")) {
-                       return new FileBucket(fs, f);
+                       return BaseFileBucket.create(fs, f);
                } else if(type.equals("PaddedEphemerallyEncryptedBucket")) {
                        return new PaddedEphemerallyEncryptedBucket(fs, random, 
f);
                } else if(type.equals("NullBucket")) {

Deleted: trunk/freenet/src/freenet/support/io/SpyInputStream.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SpyInputStream.java    2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/SpyInputStream.java    2007-07-21 
01:42:16 UTC (rev 14222)
@@ -1,148 +0,0 @@
-package freenet.support.io;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/*
- * This code is part of FProxy, an HTTP proxy server for Freenet. It is
- * distributed under the GNU Public Licence (GPL) version 2. See
- * http://www.gnu.org/ for further details of the GPL.
- */
-
-/**
- * The purpose of all of this gobbledeygook is to keep rude FCPClient
- * implementations from writing to temp files after the requests that own them
- * have been canceled.
- * 
- * <p>
- * These could be removed once FCPClient implementation deficiencies have been
- * corrected but sanity checks are always useful.
- * </p>
- * 
- * @author ian
- * @see freenet.support.SpyOutputStream
- */
-class SpyInputStream extends java.io.FilterInputStream {
-
-    private String prefix = "";
-       private TempFileBucket tfb = null;
-
-       private final void println(String text) {
-       }
-
-       private final void checkValid() throws IOException {
-               synchronized (tfb) {
-                       if (tfb.isReleased()) {
-                               throw new IOException(
-                                       "Attempt to use a released 
TempFileBucket: " + prefix);
-                       }
-               }
-       }
-
-       /**
-        * Constructor for the SpyInputStream object
-        * 
-        * @param tfb
-        * @param prefix
-        * @exception IOException
-        */
-       public SpyInputStream(TempFileBucket tfb, String prefix)
-               throws IOException {
-               super(null);
-               InputStream tmpIn = null;
-               try {
-                       this.prefix = prefix;
-                       this.tfb = tfb;
-                       checkValid();
-                       tmpIn = tfb.getRealInputStream();
-                       in = tmpIn;
-               } catch (IOException ioe) {
-                       try {
-                               if (tmpIn != null) {
-                                       tmpIn.close();
-                               }
-                       } catch (Exception e) {
-                               // NOP
-                       }
-                       throw ioe;
-               }
-               println("Created new InputStream");
-       }
-
-       public int read() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read()");
-                       checkValid();
-                       return in.read();
-               }
-       }
-
-       public int read(byte[] bytes) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read(byte[])");
-                       checkValid();
-                       return in.read(bytes);
-               }
-       }
-
-    public int read(byte[] bytes, int a, int b) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".read(byte[], int, int)");
-                       checkValid();
-                       // FIXME remove debugging
-                       if((a+b > bytes.length) || (a < 0) || (b < 0))
-                               throw new 
ArrayIndexOutOfBoundsException("a="+a+", b="+b+", length "+bytes.length);
-                       return in.read(bytes, a, b);
-               }
-       }
-    
-       public long skip(long a) throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".skip(long)");
-                       checkValid();
-                       return in.skip(a);
-               }
-       }
-
-    public int available() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".available()");
-                       checkValid();
-                       return in.available();
-               }
-       }
-       
-       public void close() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".close()");
-                       checkValid();
-                       in.close();
-                       if (tfb.streams.contains(in)) {
-                               tfb.streams.removeElement(in);
-                       }
-               }
-       }
-
-       
-       public void mark(int a) {
-               synchronized (tfb) {
-                       println(".mark(int)");
-                       in.mark(a);
-               }
-       }
-
-       public void reset() throws java.io.IOException {
-               synchronized (tfb) {
-                       println(".reset()");
-                       checkValid();
-                       in.reset();
-               }
-       }
-
-    public boolean markSupported() {
-               synchronized (tfb) {
-                       println(".markSupported()");
-                       return in.markSupported();
-               }
-       }
-}

Deleted: trunk/freenet/src/freenet/support/io/SpyOutputStream.java
===================================================================
--- trunk/freenet/src/freenet/support/io/SpyOutputStream.java   2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/SpyOutputStream.java   2007-07-21 
01:42:16 UTC (rev 14222)
@@ -1,116 +0,0 @@
-package freenet.support.io;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import freenet.support.Logger;
-
-/*
- * This code is part of FProxy, an HTTP proxy server for Freenet. It is
- * distributed under the GNU Public Licence (GPL) version 2. See
- * http://www.gnu.org/ for further details of the GPL.
- */
-
-/**
- * @author ian
- * @see freenet.support.SpyInputStream
- */
-public class SpyOutputStream extends FilterOutputStream {
-
-       private String prefix = "";
-       private TempFileBucket tfb = null;
-
-       private void debugPrintLn(String text) {
-               if (Logger.shouldLog(Logger.DEBUG,this))
-                       Logger.debug(this, text);
-       }
-
-       private final void checkValid() throws IOException {
-               synchronized (tfb) {
-                       if (tfb.isReleased()) {
-                               throw new IOException(
-                                       "Attempt to use a released 
TempFileBucket: " + prefix);
-                       }
-               }
-       }
-
-       /**
-        * Constructor for the SpyOutputStream object
-        * 
-        * @param tfb
-        * @param pref
-        * @exception IOException
-        */
-       public SpyOutputStream(TempFileBucket tfb, String pref)
-               throws IOException {
-               super(null);
-               OutputStream tmpOut = null;
-               try {
-                       this.prefix = pref;
-                       this.tfb = tfb;
-                       checkValid();
-                       tmpOut = tfb.getRealOutputStream();
-                       out = tmpOut;
-               } catch (IOException ioe) {
-                       //ioe.printStackTrace();
-                       debugPrintLn("SpyOutputStream ctr failed!: " + 
ioe.toString());
-                       try {
-                               if (tmpOut != null) {
-                                       tmpOut.close();
-                               }
-                       } catch (Exception e0) {
-                               // NOP
-                       }
-                       debugPrintLn("SpyOutputStream ctr failed!: " + 
ioe.toString());
-                       throw ioe;
-               }
-               debugPrintLn("Created new OutputStream");
-       }
-
-       ////////////////////////////////////////////////////////////
-       // FilterOutputStream implementation
-
-       public void write(int b) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(b)");
-                       checkValid();
-                       out.write(b);
-               }
-       }
-
-       public void write(byte[] buf) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(buf)");
-                       checkValid();
-                       out.write(buf);
-               }
-       }
-
-       public void write(byte[] buf, int off, int len) throws IOException {
-               synchronized (tfb) {
-                       //       println(".write(buf,off,len)");
-                       checkValid();
-                       out.write(buf, off, len);
-               }
-       }
-
-       public void flush() throws IOException {
-               synchronized (tfb) {
-                       debugPrintLn(".flush()");
-                       checkValid();
-                       out.flush();
-               }
-       }
-
-       public void close() throws IOException {
-               synchronized (tfb) {
-                       debugPrintLn(".close()");
-                       checkValid();
-                       out.close();
-                       if (tfb.streams.contains(out)) {
-                               tfb.streams.removeElement(out);
-                       }
-               }
-       }
-}

Modified: trunk/freenet/src/freenet/support/io/TempBucketFactory.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TempBucketFactory.java 2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/TempBucketFactory.java 2007-07-21 
01:42:16 UTC (rev 14222)
@@ -1,6 +1,5 @@
 package freenet.support.io;

-import java.io.File;
 import java.io.IOException;

 import freenet.support.Logger;
@@ -20,24 +19,13 @@
  */
 public class TempBucketFactory implements BucketFactory {

-       private static class NOPHook implements TempBucketHook {
-               public void enlargeFile(long curLength, long finalLength) {
-               }
-               public void shrinkFile(long curLength, long finalLength) {
-               }
-               public void deleteFile(long curLength) {
-               }
-               public void createFile(long curLength) {
-               }
-       }
-
-       private final static TempBucketHook DONT_HOOK = new NOPHook();
-       private static TempBucketHook hook = DONT_HOOK;
        private static boolean logDebug=true;

        private final FilenameGenerator filenameGenerator;

        public static long defaultIncrement = 4096;
+       
+       public static float DEFAULT_FACTOR = 1.25F;

        // Storage accounting disabled by default.
        public TempBucketFactory(FilenameGenerator filenameGenerator) {
@@ -46,7 +34,7 @@
        }

        public Bucket makeBucket(long size) throws IOException {
-               return makeBucket(size, 1.25F, defaultIncrement);
+               return makeBucket(size, DEFAULT_FACTOR, defaultIncrement);
        }

        public Bucket makeBucket(long size, float factor) throws IOException {
@@ -68,10 +56,9 @@
        public TempFileBucket makeBucket(long size, float factor, long 
increment)
                throws IOException {
                logDebug = Logger.shouldLog(Logger.DEBUG,this);
-               File f = filenameGenerator.makeRandomFilename();
-               if(f == null) throw new NullPointerException();
+               long id = filenameGenerator.makeRandomFilename();

-               return new TempFileBucket(f, hook, size, increment, factor);
+               return new TempFileBucket(id, filenameGenerator);
        }

        /**
@@ -97,31 +84,4 @@
                        }
                }
        }
-
-       /**
-        * Sets the storage accounting hook.
-        * 
-        * @param t
-        *            The hook object to use to keep track of the amount of 
storage
-        *            used. It is legal for t to be null. In this case storage
-        *            accounting is disabled.
-        */
-       public static void setHook(TempBucketHook t) {
-               if (logDebug)
-                       Logger.debug(
-                               TempBucketFactory.class,
-                               "Set TempBucketHook to " + t);
-               hook = t;
-               if (hook == null) {
-                       // Allow hooks to be disabled w/o sprinkling
-                       // if (hook != null) {/*blah blah */} calls
-                       // throughout the code.
-                       hook = DONT_HOOK;
-                       if (logDebug) {
-                               Logger.debug(
-                                       TempBucketHook.class,
-                                       "TempBucketHook file usage management 
was disabled.");
-                       }
-               }
-       }
 }

Deleted: trunk/freenet/src/freenet/support/io/TempBucketHook.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TempBucketHook.java    2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/TempBucketHook.java    2007-07-21 
01:42:16 UTC (rev 14222)
@@ -1,34 +0,0 @@
-/* This code is part of Freenet. It is distributed under the GNU General
- * Public License, version 2 (or at your option any later version). See
- * http://www.gnu.org/ for further details of the GPL. */
-package freenet.support.io;
-
-import java.io.IOException;
-
-public interface TempBucketHook {
-    
-    /** Allocate space for a write making the file larger
-     * Call this before writing a file, make sure you call shrinkFile if the 
write fails
-     * @param curLength the length of the file before the write
-     * @param finalLength the length of the file after the write
-     * @throws IOException if insufficient space
-     */
-    void enlargeFile(long curLength, long finalLength) throws IOException;
-    
-    /** Deallocate space after a write enlarging the file fails
-     * Call this if enlargeFile was called but the write failed.
-     * Also call it if you want to truncate a file
-     * @param curLength original length before write
-     * @param finalLength length the file would have been if the write had 
succeeded
-     */
-    void shrinkFile(long curLength, long finalLength);
-    
-    /** Deallocate space for a temp file, AFTER successful delete completed
-     */
-    void deleteFile(long curLength);
-    
-    /** Allocate space for a temp file, before actually creating it
-     */
-    void createFile(long curLength) throws IOException;
-
-}

Modified: trunk/freenet/src/freenet/support/io/TempFileBucket.java
===================================================================
--- trunk/freenet/src/freenet/support/io/TempFileBucket.java    2007-07-21 
00:23:34 UTC (rev 14221)
+++ trunk/freenet/src/freenet/support/io/TempFileBucket.java    2007-07-21 
01:42:16 UTC (rev 14222)
@@ -8,6 +8,7 @@

 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
+import freenet.support.api.Bucket;

 /*
  *  This code is part of FProxy, an HTTP proxy server for Freenet.
@@ -19,138 +20,49 @@
  *
  * @author     giannij
  */
-public class TempFileBucket extends FileBucket {
-       TempBucketHook hook = null;
-       // How much we have asked the Hook to allocate for us
-       long fakeLength = 0;
-       long minAlloc;
-       float factor;
+public class TempFileBucket extends BaseFileBucket implements Bucket, 
SerializableToFieldSetBucket {
+       long filenameID;
+       final FilenameGenerator generator;
        private static boolean logDebug = true;
+       private boolean readOnly;
        /**
         * Constructor for the TempFileBucket object
         *
         * @param  f  File
         */
-       protected TempFileBucket(
-               File f,
-               TempBucketHook hook,
-               long startLength,
-               long minAlloc,
-               float factor)
-               throws IOException {
-               super(f, false, false, true, true, true);
+       public TempFileBucket(
+               long id,
+               FilenameGenerator generator) {
+               super(generator.getFilename(id));
+               this.filenameID = id;
+               this.generator = generator;
                synchronized(this) {
                        logDebug = Logger.shouldLog(Logger.DEBUG, this);
                }
-               if (minAlloc > 0)
-                       this.minAlloc = minAlloc;
-               else
-                       this.minAlloc = 1024;
-               this.factor = factor;
-               if (factor < 1.0)
-                       throw new IllegalArgumentException("factor must be >= 
1.0");

-               // Make sure finalize wacks temp file 
-               // if it is not explictly freed.
-               deleteOnFinalize = true;
-
                //System.err.println("FProxyServlet.TempFileBucket -- created: 
" +
                //         f.getAbsolutePath());
-               this.hook = hook;
-               long x = startLength <= 0 ? minAlloc : startLength;
-               hook.createFile(x);
-               this.fakeLength = x;
                synchronized(this) {
                        if (logDebug)
                                Logger.debug(
                                        this,
-                                       "Initializing TempFileBucket(" + f + 
',' + hook + ')');
+                                       "Initializing TempFileBucket(" + 
getFile());
                }
        }

-       /**
-        *  Gets the realInputStream attribute of the TempFileBucket object
-        *
-        * @return                  The realInputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       synchronized InputStream getRealInputStream() throws IOException {
-               if (released || closing)
-                       throw new IllegalStateException(
-                               "Trying to getRealInputStream on released 
TempFileBucket "+this+" !");
-               if (logDebug)
-                       Logger.debug(
-                               this,
-                               "getRealInputStream() for " + file,
-                               new Exception("debug"));
-               if (!file.exists())
-                       return new NullInputStream();
-               else
-                       return new HookedFileBucketInputStream(file);
+       protected boolean deleteOnFinalize() {
+               // Make sure finalize wacks temp file 
+               // if it is not explictly freed.
+               return true;
        }
-
+       
        /**
-        *  Gets the realOutputStream attribute of the TempFileBucket object
-        *
-        * @return                  The realOutputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       OutputStream getRealOutputStream() throws IOException {
-               if (released || closing)
-                       throw new IllegalStateException(
-                               "Trying to getRealOutputStream on released 
TempFileBucket "+this+" !");
-               synchronized(this) {
-                       if (logDebug)
-                               Logger.debug(
-                                       this,
-                                       "getRealOutputStream() for " + file,
-                                       new Exception("debug"));
-               }
-               return super.getOutputStream();
-       }
-
-       // Wrap non-const members so we can tell
-       // when code touches the Bucket after it
-       // has been released.
-       /**
-        *  Gets the inputStream attribute of the TempFileBucket object
-        *
-        * @return                  The inputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       public synchronized InputStream getInputStream() throws IOException {
-               if (released || closing)
-                       throw new IllegalStateException(
-                               "Trying to getInputStream on released 
TempFileBucket "+this+" !");
-               logDebug = Logger.shouldLog(Logger.DEBUG, this);
-               if (logDebug)
-                       Logger.debug(this, "getInputStream for " + file);
-               InputStream newIn = new SpyInputStream(this, 
file.getAbsolutePath());
-               return newIn;
-       }
-
-       /**
-        *  Gets the outputStream attribute of the TempFileBucket object
-        *
-        * @return                  The outputStream value
-        * @exception  IOException  Description of the Exception
-        */
-       public synchronized OutputStream getOutputStream() throws IOException {
-               if (released || closing)
-                       throw new IllegalStateException(
-                               "Trying to getOutputStream on released 
TempFileBucket "+this+" !");
-               logDebug = Logger.shouldLog(Logger.DEBUG, this);
-               if (logDebug)
-                       Logger.debug(this, "getOutputStream for " + file);
-               return new SpyOutputStream(this, file.getAbsolutePath());
-       }
-
-       /**
         *  Release
         *
         * @return    Success
         */
        public synchronized boolean release() {
+               File file = getFile();
                if(Logger.shouldLog(Logger.MINOR, this))
                        Logger.minor(this, "Releasing bucket: "+file, new 
Exception("debug"));
                //System.err.println("FProxyServlet.TempFileBucket -- release: 
" +                      // file.getAbsolutePath());
@@ -235,13 +147,7 @@
                                        new Exception());
                                // Nonrecoverable; even though the user can't 
fix it it's still very serious
                                return false;
-                       } else {
-                               if (hook != null)
-                                       hook.deleteFile(fakeLength);
                        }
-               } else {
-                       if (hook != null)
-                               hook.deleteFile(fakeLength);
                }
                if (logDebug)
                        Logger.debug(
@@ -274,179 +180,55 @@
        private boolean released;
        private boolean closing;

-       protected synchronized FileBucketOutputStream newFileBucketOutputStream(
-               String s,
-               boolean append,
-               long restartCount)
-               throws IOException {
-               if (logDebug)
-                       Logger.debug(this,
-                               "Creating new HookedFileBucketOutputStream for 
" + file);
-               if (hook != null)
-                       return new HookedFileBucketOutputStream(s, 
restartCount);
-               else
-                       return super.newFileBucketOutputStream(new File(s), s, 
restartCount);
-       }
-
        protected synchronized void deleteFile() {
                if (logDebug)
-                       Logger.debug(this, "Deleting " + file);
-               file.delete();
-               if (hook != null)
-                       hook.deleteFile(fakeLength);
+                       Logger.debug(this, "Deleting " + getFile());
+               getFile().delete();
        }

        protected synchronized void resetLength() {
                if (logDebug)
-                       Logger.debug(this, "Resetting length for " + file);
-               if (length != 0) {
-                       if (hook != null) {
-                               hook.shrinkFile(0, fakeLength);
-                               fakeLength = 0;
-                       }
-                       super.resetLength();
-               }
+                       Logger.debug(this, "Resetting length for " + getFile());
        }

        protected final synchronized void getLengthSynchronized(long len) 
throws IOException {
                //       Core.logger.log(this, "getLengthSynchronized("+len+
                //                    "); fakeLength = "+fakeLength, 
Logger.DEBUG);
-               long l = fakeLength;
-               long ol = l;
-               while (len > l) {
-                       l = (long) (l * factor);
-                       if (minAlloc > 0)
-                               l = l + minAlloc - (l % minAlloc);
-                       if (l <= fakeLength)
-                               throw new IllegalStateException("Bucket 
extension error!");
-                       //        Core.logger.log(this, "l now "+l, 
Logger.DEBUG);
-                       if (ol == l)
-                               throw new IllegalStateException("infinite 
loop");
-                       ol = l;
-               }
-               if (fakeLength != l) {
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               Logger.debug(
-                                       this,
-                                       "getLengthSynchronized("
-                                               + len
-                                               + "): increasing "
-                                               + fakeLength
-                                               + " to: "
-                                               + l
-                                               + " (real length: "
-                                               + length
-                                               + ')');
-                       hook.enlargeFile(fakeLength, l);
-               }
-               fakeLength = l;
+               if(len <= 0) return;
+               length += len;
        }

        public synchronized String toString(){
-               return "TempFileBucket (File: 
'"+getFile().getAbsolutePath()+"', streams: "+streams.size()+", hook: "+hook+ 
')';
+               return "TempFileBucket (File: 
'"+getFile().getAbsolutePath()+"', streams: "+streams.size();
        }

-       class HookedFileBucketInputStream extends FileBucketInputStream {
-               HookedFileBucketInputStream(File f) throws IOException {
-                       super(f);
-                       streams.addElement(this);
-               }
+       public SimpleFieldSet toFieldSet() {
+               SimpleFieldSet fs = super.toFieldSet();
+               fs.putSingle("Type", "TempFileBucket");
+               return fs;
+       }

-               public void close() throws IOException {
-                       super.close();
-                       while (streams.remove(this));
-               }
+       protected boolean createFileOnly() {
+               return false;
        }

-       class HookedFileBucketOutputStream extends FileBucketOutputStream {
+       protected boolean deleteOnFree() {
+               return true;
+       }

-               protected HookedFileBucketOutputStream(
-                       String s,
-                       long restartCount)
-                       throws IOException {
-                       super(new File(s), s, restartCount);
-                       streams.addElement(this);
-                       if (Logger.shouldLog(Logger.DEBUG, this))
-                               Logger.debug(
-                                       this,
-                                       "Created HookedFileBucketOutputStream("
-                                               + s
-                                               + ','
-                            + restartCount
-                                               + ')');
-               }
+       public File getFile() {
+               return generator.getFilename(filenameID);
+       }

-               public void close() throws IOException {
-                       super.close();
-                       while (streams.remove(this));
-               }
+       public boolean isReadOnly() {
+               return readOnly;
+       }

-               public void write(byte[] b) throws IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(byte[] len "+
-                       //                        b.length+") for "+file, 
Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               //            Core.logger.log(this, 
"Synchronized on TempFileBucket", 
-                               //                            Logger.DEBUG);
-                               super.confirmWriteSynchronized();
-                               //            Core.logger.log(this, 
"confirmWriteSynchronized()", Logger.DEBUG);
-                               long finalLength = length + b.length;
-                               //            Core.logger.log(this, 
"length="+length+", finalLength="+finalLength,
-                               //                            Logger.DEBUG);
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               //            Core.logger.log(this, "Called 
hook.enlargeFile()", Logger.DEBUG);
-                               try {
-                                       super.write(b);
-                                       //                Core.logger.log(this, 
"Written", Logger.DEBUG);
-                               } catch (IOException e) {
-                                       //                Core.logger.log(this, 
"Write failed", Logger.DEBUG);
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       //                Core.logger.log(this, 
"Shrank file", Logger.DEBUG);
-                                       throw e;
-                               }
-                       }
-               }
-
-               public void write(byte[] b, int off, int len) throws 
IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(byte[], "+off+
-                       //                        ","+len+") for "+file, 
Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               long finalLength = length + len;
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               try {
-                                       super.write(b, off, len);
-                               } catch (IOException e) {
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       throw e;
-                               }
-                       }
-               }
-
-               public void write(int b) throws IOException {
-                       //        Core.logger.log(this, 
"HookedFileBucketOutputStream.write(int) for "+file,
-                       //                        Logger.DEBUG);
-                       synchronized (TempFileBucket.this) {
-                               long finalLength = length + 1;
-                               long realStartLen = fakeLength;
-                               getLengthSynchronized(finalLength);
-                               try {
-                                       super.write(b);
-                               } catch (IOException e) {
-                                       hook.shrinkFile(realStartLen, 
fakeLength);
-                                       fakeLength = realStartLen;
-                                       throw e;
-                               }
-                       }
-               }
-
+       public void setReadOnly() {
+               readOnly = true;
        }

-       public SimpleFieldSet toFieldSet() {
-               SimpleFieldSet fs = super.toFieldSet();
-               fs.putSingle("Type", "TempFileBucket");
-               return fs;
+       protected boolean deleteOnExit() {
+               return true;
        }
 }


Reply via email to