adammurdoch 2003/02/12 20:28:46 Modified: vfs/src/java/org/apache/commons/vfs FileContent.java Resources.properties vfs/src/java/org/apache/commons/vfs/impl VirtualFileSystem.java vfs/src/java/org/apache/commons/vfs/provider AbstractFileObject.java DefaultFileContent.java DelegateFileObject.java vfs/src/java/org/apache/commons/vfs/provider/ftp FtpFileObject.java vfs/src/test/org/apache/commons/vfs/provider/test JunctionTests.java vfs/src/test/org/apache/commons/vfs/test ContentTests.java ProviderWriteTests.java Added: vfs/src/java/org/apache/commons/vfs/util MonitorInputStream.java MonitorOutputStream.java Log: - FileContent now allows multiple readers. - Tidied-up the interface between AbstractFileObject and its subclasses: - Added onChildrenChanged() and onChange() to deal with way-too-overloaded meaning of onDetach(). - Replaced invalidateChildren() with childrenChanged(). - All doBlah() methods now throw Exception, rather than a mix of exceptions. - Axed doInputEnd() and doOutputEnd(). Revision Changes Path 1.7 +8 -7 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/FileContent.java Index: FileContent.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/FileContent.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- FileContent.java 12 Feb 2003 07:56:09 -0000 1.6 +++ FileContent.java 13 Feb 2003 04:28:45 -0000 1.7 @@ -62,15 +62,16 @@ /** * Represents the data content of a file. * - * <p>To read from a file, use the {@link #getInputStream} method. + * <p>To read from a file, use the <code>InputStream</code> returned by + * {@link #getInputStream}. * - * <p>To write to a file, use the {@link #getOutputStream} method. This - * method will create the file and the parent folder, if necessary. + * <p>To write to a file, use the <code>OutputStream</code> returned by + * {@link #getOutputStream} method. This will create the file, and the parent + * folder, if necessary. * - * <p>To prevent concurrency problems, only a single <code>InputStream</code>, - * or <code>OutputStream</code> may be open at any time, for each file. - * - * <p>TODO - allow multiple input streams? + * <p>To prevent concurrency problems, a file may not have an OutputStream and + * an InputStream open at the same time. A file may have multiple InputStreams + * open at the sametime. * * @see FileObject#getContent * 1.15 +9 -3 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/Resources.properties Index: Resources.properties =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/Resources.properties,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- Resources.properties 25 Nov 2002 05:30:59 -0000 1.14 +++ Resources.properties 13 Feb 2003 04:28:45 -0000 1.15 @@ -22,7 +22,6 @@ vfs.provider/create-file.error=Could not create file "{0}". vfs.provider/write-read-only.error=Could not write to "{0}" because it is read-only. vfs.provider/write-folder.error=Could not write to "{0}" because it is a folder. -vfs.provider/write-in-use.error=Could not write to "{0}" because it is already in use. vfs.provider/write.error=Could not write to "{0}". vfs.provider/copy-file.error=Could not copy "{0}" to "{1}". vfs.provider/copy-read-only.error=Could not copy {0} "{1}" to "{2}" because the destination file is read-only. @@ -31,6 +30,7 @@ vfs.provider/check-is-writeable.error=Could not determine if file "{0}" is writeable. vfs.provider/check-is-readable.error=Could not determine if file "{0}" is readable. vfs.provider/get-url.error=Could not create URL for "{0}". +vfs.provider/close.error=Could not close "{0}". # DefaultFileContent vfs.provider/get-size-folder.error=Could not determine the size of "{0}" because it is a folder. @@ -39,13 +39,15 @@ vfs.provider/get-size.error=Could not determine the size of file "{0}". vfs.provider/read-folder.error=Could not read from "{0}" because it is a folder. vfs.provider/read-no-exist.error=Could not read file "{0}" because it does not exist. -vfs.provider/read-in-use.error=Could not read file "{0}" because it is already being used. +vfs.provider/read-in-use.error=Could not read file "{0}" because it is currently being written to. vfs.provider/read.error=Could not read file "{0}". +vfs.provider/write-in-use.error=Could not write to "{0}" because it is currently in use. vfs.provider/get-last-modified-no-exist.error=Could not determine the last modified timestamp of "{0}" because it does not exist. vfs.provider/get-last-modified.error=Could not determine the last modified timestamp of "{0}". vfs.provider/set-last-modified-no-exist.error=Could not set the last modified timestamp of "{0}" because it does not exist. vfs.provider/set-last-modified.error=Could not set the last modified timestamp of "{0}". -vfs.provider/get-certificates-no-exist.error=Could not retrive the certificates of "{0}" because it does not exist. +vfs.provider/get-certificates-no-exist.error=Could not retrieve the certificates of "{0}" because it does not exist. +vfs.provider/get-certificates.error=Could not retrieve the certificates of "{0}". vfs.provider/close-instr.error=Could not close the input stream for file "{0}". vfs.provider/close-outstr.error=Could not close the output stream for file "{0}". vfs.provider/get-attribute.error=Could not get attribute "{0}" of "{1}". @@ -89,6 +91,10 @@ vfs.impl/pkg-sealing-unsealed=Trying to seal package "{0}" that exists as unsealed. vfs.impl/pkg-sealed-other-url=Package "{0}" exists and is sealed with other URL. +# VirtualFileSystem +vfs.impl/nested-junction.error=Attempting to create a nested junction at "{0}". Nested junctions are not supported. +vfs.impl/create-junction.error=Could not create a junction at "{0}". +r # Local Provider vfs.provider.local/get-type.error=Could not determine the type of "{0}". vfs.provider.local/delete-file.error=Could not delete "{0}". 1.6 +29 -22 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/impl/VirtualFileSystem.java Index: VirtualFileSystem.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/impl/VirtualFileSystem.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- VirtualFileSystem.java 12 Feb 2003 07:56:10 -0000 1.5 +++ VirtualFileSystem.java 13 Feb 2003 04:28:45 -0000 1.6 @@ -142,33 +142,40 @@ // Check for nested junction - these are not supported yet if ( getJunctionForFile( junctionName ) != null ) { - throw new FileSystemException( "impl/nested-junction.error", junctionName ); + throw new FileSystemException( "vfs.impl/nested-junction.error", junctionName ); } - // Add to junction table - junctions.put( junctionName, targetFile ); - - // Create ancestors of junction point - FileName childName = junctionName; - boolean done = false; - for ( FileName parentName = childName.getParent(); - !done && parentName != null; - childName = parentName, parentName = parentName.getParent() ) + try { - DelegateFileObject file = (DelegateFileObject)getFile( parentName ); - if ( file == null ) - { - file = new DelegateFileObject( parentName, this, null ); - putFile( file ); - } - else + // Add to junction table + junctions.put( junctionName, targetFile ); + + // Create ancestors of junction point + FileName childName = junctionName; + boolean done = false; + for ( FileName parentName = childName.getParent(); + !done && parentName != null; + childName = parentName, parentName = parentName.getParent() ) { - done = file.exists(); + DelegateFileObject file = (DelegateFileObject)getFile( parentName ); + if ( file == null ) + { + file = new DelegateFileObject( parentName, this, null ); + putFile( file ); + } + else + { + done = file.exists(); + } + file.attachChild( childName.getBaseName() ); } - file.attachChild( childName.getBaseName() ); - } - // TODO - attach all cached children of the junction point to their real file + // TODO - attach all cached children of the junction point to their real file + } + catch ( final Exception e ) + { + throw new FileSystemException( "vfs.impl/create-junction.error", junctionName ); + } } /** 1.24 +92 -63 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/AbstractFileObject.java Index: AbstractFileObject.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/AbstractFileObject.java,v retrieving revision 1.23 retrieving revision 1.24 diff -u -r1.23 -r1.24 --- AbstractFileObject.java 12 Feb 2003 07:56:12 -0000 1.23 +++ AbstractFileObject.java 13 Feb 2003 04:28:45 -0000 1.24 @@ -110,6 +110,8 @@ * Attaches this file object to its file resource. This method is called * before any of the doBlah() or onBlah() methods. Sub-classes can use * this method to perform lazy initialisation. + * + * This implementation does nothing. */ protected void doAttach() throws Exception { @@ -118,10 +120,12 @@ /** * Detaches this file object from its file resource. * - * <p>Called when this file is closed, or its type changes. Note that - * the file object may be reused later, so should be able to be reattached. + * <p>Called when this file is closed. Note that the file object may be + * reused later, so should be able to be reattached. + * + * This implementation does nothing. */ - protected void doDetach() throws FileSystemException + protected void doDetach() throws Exception { } @@ -134,7 +138,9 @@ /** * Determines if this file can be read. Is only called if {@link #doGetType} - * does not return null. This implementation always returns true. + * does not return null. + * + * This implementation always returns true. */ protected boolean doIsReadable() throws Exception { @@ -143,8 +149,9 @@ /** * Determines if this file can be written to. Is only called if - * {@link #doGetType} does not return null. This implementation always - * returns true. + * {@link #doGetType} does not return null. + * + * This implementation always returns true. */ protected boolean doIsWriteable() throws Exception { @@ -165,6 +172,8 @@ * <li>{@link #doIsWriteable} returns true. * <li>This file has no children, if a folder. * </ul> + * + * This implementation throws an exception. */ protected void doDelete() throws Exception { @@ -178,6 +187,8 @@ * <li>The parent folder exists and is writeable, or this file is the * root of the file system. * </ul> + * + * This implementation throws an exception. */ protected void doCreateFolder() throws Exception { @@ -185,16 +196,29 @@ } /** - * Called when the children of this file change. + * Called when the children of this file change. Allows subclasses to + * refresh any cached information about the children of this file. + * + * This implementation does nothing. + */ + protected void onChildrenChanged() throws Exception + { + } + + /** + * Called when the type or content of this file changes. + * + * This implementation does nothing. */ - protected void onChildrenChanged() + protected void onChange() throws Exception { } /** * Returns the last modified time of this file. Is only called if - * {@link #doGetType} does not return null. This implementation throws - * an exception. + * {@link #doGetType} does not return null. + * + * This implementation throws an exception. */ protected long doGetLastModifiedTime() throws Exception { @@ -203,10 +227,11 @@ /** * Sets the last modified time of this file. Is only called if - * {@link #doGetType} does not return null. This implementation - * throws an exception. + * {@link #doGetType} does not return null. + * + * This implementation throws an exception. */ - protected void doSetLastModifiedTime( long modtime ) + protected void doSetLastModifiedTime( final long modtime ) throws Exception { throw new FileSystemException( "vfs.provider/set-last-modified-not-supported.error" ); @@ -214,7 +239,9 @@ /** * Gets an attribute of this file. Is only called if {@link #doGetType} - * does not return null. This implementation return null. + * does not return null. + * + * This implementation always returns null. */ protected Object doGetAttribute( final String attrName ) throws Exception @@ -224,9 +251,11 @@ /** * Sets an attribute of this file. Is only called if {@link #doGetType} - * does not return null. This implementation throws an exception. + * does not return null. + * + * This implementation throws an exception. */ - protected void doSetAttribute( String atttrName, Object value ) + protected void doSetAttribute( final String atttrName, final Object value ) throws Exception { throw new FileSystemException( "vfs.provider/set-attribute-not-supported.error" ); @@ -234,10 +263,11 @@ /** * Returns the certificates used to sign this file. Is only called if - * {@link #doGetType} does not return null. This implementation returns - * null. + * {@link #doGetType} does not return null. + * + * This implementation always returns null. */ - protected Certificate[] doGetCertificates() throws FileSystemException + protected Certificate[] doGetCertificates() throws Exception { return null; } @@ -252,8 +282,8 @@ * Creates an input stream to read the file content from. Is only called * if {@link #doGetType} returns {@link FileType#FILE}. * - * <p>There is guaranteed never to be more than one stream for this file - * (input or output) open at any given time. + * <p>It is guaranteed that there are no open output streams for this file + * when this method is called. * * <p>The returned stream does not have to be buffered. */ @@ -269,10 +299,12 @@ * and is a folder. * </ul> * - * <p>There is guaranteed never to be more than one stream for this file - * (input or output) open at any given time. + * <p>It is guaranteed that there are no open stream (input or output) for + * this file when this method is called. * * <p>The returned stream does not have to be buffered. + * + * This implementation throws an exception. */ protected OutputStream doGetOutputStream() throws Exception { @@ -280,22 +312,6 @@ } /** - * Notification of the output stream being closed. - * TODO - get rid of this. - */ - protected void doEndOutput() throws Exception - { - } - - /** - * Notification of the input stream being closed. - * TODO - get rid of this. - */ - protected void doEndInput() throws Exception - { - } - - /** * Returns the URI of the file. */ public String toString() @@ -552,22 +568,22 @@ throw new FileSystemException( "vfs.provider/delete-read-only.error", name ); } - // Delete the file try { + // Delete the file doDelete(); + + // Update cached info + handleDelete(); } - catch ( RuntimeException re ) + catch ( final RuntimeException re ) { throw re; } - catch ( Exception exc ) + catch ( final Exception exc ) { throw new FileSystemException( "vfs.provider/delete.error", new Object[]{name}, exc ); } - - // Update cached info - handleDelete(); } /** @@ -664,10 +680,13 @@ parent.createFolder(); } - // Create the folder try { + // Create the folder doCreateFolder(); + + // Update cached info + handleCreate( FileType.FOLDER ); } catch ( final RuntimeException re ) { @@ -677,9 +696,6 @@ { throw new FileSystemException( "vfs.provider/create-folder.error", name, exc ); } - - // Update cached info - handleCreate( FileType.FOLDER ); } /** @@ -829,7 +845,14 @@ } // Detach from the file - detach(); + try + { + detach(); + } + catch ( final Exception e ) + { + exc = new FileSystemException( "vfs.provider/close.error", name, e ); + } if ( exc != null ) { @@ -883,7 +906,7 @@ * Detaches this file, invaliating all cached info. This will force * a call to {@link #doAttach} next time this file is used. */ - protected void detach() throws FileSystemException + private void detach() throws Exception { if ( attached ) { @@ -932,22 +955,23 @@ */ protected void endOutput() throws Exception { - boolean newFile = ( type == null ); - - doEndOutput(); - - if ( newFile ) + if ( type == null ) { // File was created handleCreate( FileType.FILE ); } + else + { + // File has changed + onChange(); + } } /** * Called when this file is created. Updates cached info and notifies * the parent and file system. */ - protected void handleCreate( final FileType newType ) + protected void handleCreate( final FileType newType ) throws Exception { // Fix up state type = newType; @@ -956,6 +980,9 @@ // Notify parent that its child list may no longer be valid notifyParent(); + // Notify subclass + onChange(); + // Notify the file system fs.fireFileCreated( this ); } @@ -964,7 +991,7 @@ * Called when this file is deleted. Updates cached info and notifies * subclasses, parent and file system. */ - protected void handleDelete() + protected void handleDelete() throws Exception { // Fix up state type = null; @@ -973,6 +1000,9 @@ // Notify parent that its child list may no longer be valid notifyParent(); + // Notify subclass + onChange(); + // Notify the file system fs.fireFileDeleted( this ); } @@ -981,7 +1011,7 @@ * Notify the parent of a change to its children, when a child is created * or deleted. */ - private void notifyParent() + private void notifyParent() throws Exception { if ( parent == null ) { @@ -991,15 +1021,15 @@ if ( parent != null ) { - parent.invalidateChildren(); + parent.childrenChanged(); } } /** - * Notifies a file that children have been created or deleted. + * Notifies the file that its children have changed. * @todo Indicate whether the child was added or removed, and which child. */ - private void invalidateChildren() + protected void childrenChanged() throws Exception { children = null; onChildrenChanged(); @@ -1078,5 +1108,4 @@ } } } - } 1.12 +55 -103 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/DefaultFileContent.java Index: DefaultFileContent.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/DefaultFileContent.java,v retrieving revision 1.11 retrieving revision 1.12 diff -u -r1.11 -r1.12 --- DefaultFileContent.java 12 Feb 2003 07:56:13 -0000 1.11 +++ DefaultFileContent.java 13 Feb 2003 04:28:45 -0000 1.12 @@ -55,15 +55,16 @@ */ package org.apache.commons.vfs.provider; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.cert.Certificate; +import java.util.ArrayList; import org.apache.commons.vfs.FileContent; import org.apache.commons.vfs.FileObject; import org.apache.commons.vfs.FileSystemException; +import org.apache.commons.vfs.util.MonitorInputStream; +import org.apache.commons.vfs.util.MonitorOutputStream; /** * The content of a file. @@ -80,7 +81,7 @@ private final AbstractFileObject file; private int state = STATE_NONE; - private FileContentInputStream instr; + private final ArrayList instrs = new ArrayList(); private FileContentOutputStream outstr; public DefaultFileContent( final AbstractFileObject file ) @@ -89,7 +90,7 @@ } /** - * Returns the file which this is the content of. + * Returns the file that this is the content of. */ public FileObject getFile() { @@ -206,7 +207,15 @@ { throw new FileSystemException( "vfs.provider/get-certificates-no-exist.error", file ); } - return file.doGetCertificates(); + + try + { + return file.doGetCertificates(); + } + catch ( final Exception e ) + { + throw new FileSystemException( "vfs.provider/get-certificates.error", file, e ); + } } /** @@ -222,26 +231,26 @@ { throw new FileSystemException( "vfs.provider/read-no-exist.error", file ); } - if ( state != STATE_NONE ) + if ( state == STATE_WRITING ) { throw new FileSystemException( "vfs.provider/read-in-use.error", file ); } // Get the raw input stream - InputStream instr; + InputStream rawInstr; try { - instr = file.doGetInputStream(); + rawInstr = file.doGetInputStream(); } - catch ( Exception exc ) + catch ( final Exception exc ) { throw new FileSystemException( "vfs.provider/read.error", new Object[]{file}, exc ); } - // TODO - reuse - this.instr = new FileContentInputStream( instr ); + final FileContentInputStream instr = new FileContentInputStream( rawInstr ); + this.instrs.add( instr ); state = STATE_READING; - return this.instr; + return instr; } /** @@ -262,7 +271,6 @@ OutputStream outstr = file.getOutputStream(); // Create wrapper - // TODO - reuse this.outstr = new FileContentOutputStream( outstr ); state = STATE_WRITING; return this.outstr; @@ -277,8 +285,9 @@ try { // Close the input stream - if ( instr != null ) + while ( instrs.size() > 0 ) { + final FileContentInputStream instr = (FileContentInputStream)instrs.remove( 0 ); instr.close(); } @@ -297,11 +306,13 @@ /** * Handles the end of input stream. */ - private void endInput() throws Exception + private void endInput( final FileContentInputStream instr ) { - instr = null; - state = STATE_NONE; - file.doEndInput(); + instrs.remove( instr ); + if ( instrs.size() == 0 ) + { + state = STATE_NONE; + } } /** @@ -318,95 +329,35 @@ * An input stream for reading content. Provides buffering, and * end-of-stream monitoring. */ - private final class FileContentInputStream extends BufferedInputStream + private final class FileContentInputStream + extends MonitorInputStream { - private boolean finished; - - FileContentInputStream( InputStream instr ) + FileContentInputStream( final InputStream instr ) { super( instr ); } /** - * Reads a character. - */ - public int read() throws IOException - { - if ( finished ) - { - return -1; - } - - int ch = super.read(); - if ( ch != -1 ) - { - return ch; - } - - // End-of-stream - close(); - return -1; - } - - /** - * Reads bytes from this input stream.error occurs. - */ - public int read( byte[] buffer, int offset, int length ) - throws IOException - { - if ( finished ) - { - return -1; - } - - int nread = super.read( buffer, offset, length ); - if ( nread != -1 ) - { - return nread; - } - - // End-of-stream - close(); - return -1; - } - - /** * Closes this input stream. */ public void close() throws FileSystemException { - if ( finished ) - { - return; - } - - // Close the stream - FileSystemException exc = null; try { super.close(); } - catch ( IOException ioe ) - { - exc = new FileSystemException( "vfs.provider/close-instr.error", file, ioe ); - } - - // Notify the file object - try - { - endInput(); - } - catch ( final Exception e ) + catch ( final IOException e ) { - exc = new FileSystemException( "vfs.provider/close-instr.error", file, e ); + throw new FileSystemException( "vfs.provider/close-instr.error", file, e ); } + } - finished = true; - - if ( exc != null ) - { - throw exc; - } + /** + * Called after the stream has been closed. + */ + protected void onClose() throws IOException + { + endInput( this ); } } @@ -414,9 +365,9 @@ * An output stream for writing content. */ private final class FileContentOutputStream - extends BufferedOutputStream + extends MonitorOutputStream { - FileContentOutputStream( OutputStream outstr ) + FileContentOutputStream( final OutputStream outstr ) { super( outstr ); } @@ -426,31 +377,32 @@ */ public void close() throws FileSystemException { - FileSystemException exc = null; - - // Close the output stream try { super.close(); } + catch ( final FileSystemException e ) + { + throw e; + } catch ( final IOException e ) { - exc = new FileSystemException( "vfs.provider/close-outstr.error", file, e ); + throw new FileSystemException( "vfs.provider/close-outstr.error", file, e ); } + } - // Notify of end of output + /** + * Called after this stream is closed. + */ + protected void onClose() throws IOException + { try { endOutput(); } catch ( final Exception e ) { - exc = new FileSystemException( "vfs.provider/close-outstr.error", file, e ); - } - - if ( exc != null ) - { - throw exc; + throw new FileSystemException( "vfs.provider/close-outstr.error", file, e ); } } } 1.4 +8 -14 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/DelegateFileObject.java Index: DelegateFileObject.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/DelegateFileObject.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- DelegateFileObject.java 12 Feb 2003 07:56:13 -0000 1.3 +++ DelegateFileObject.java 13 Feb 2003 04:28:45 -0000 1.4 @@ -91,21 +91,15 @@ this.file = file; } - public void attachChild( final String baseName ) throws FileSystemException + /** Adds a child to this file. */ + public void attachChild( final String baseName ) throws Exception { if ( children.add( baseName ) ) { - detach(); + childrenChanged(); } } - public void setFile( final FileObject file ) throws FileSystemException - { - this.file = file; - children.clear(); - detach(); - } - /** * Attaches this file object to its file resource. */ @@ -120,7 +114,7 @@ /** * Detaches this file object from its file resource. */ - protected void doDetach() throws FileSystemException + protected void doDetach() throws Exception { if ( file != null ) { @@ -247,7 +241,7 @@ /** * Called from {@link DefaultFileContent#getCertificates}. */ - protected Certificate[] doGetCertificates() throws FileSystemException + protected Certificate[] doGetCertificates() throws Exception { return file.getContent().getCertificates(); } @@ -288,7 +282,7 @@ /** * Called when a file is created. */ - public void fileCreated( final FileChangeEvent event ) throws FileSystemException + public void fileCreated( final FileChangeEvent event ) throws Exception { handleCreate( file.getType() ); } @@ -296,7 +290,7 @@ /** * Called when a file is deleted. */ - public void fileDeleted( final FileChangeEvent event ) + public void fileDeleted( final FileChangeEvent event ) throws Exception { handleDelete(); } 1.13 +89 -44 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java Index: FtpFileObject.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- FtpFileObject.java 12 Feb 2003 07:56:15 -0000 1.12 +++ FtpFileObject.java 13 Feb 2003 04:28:45 -0000 1.13 @@ -64,6 +64,8 @@ import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileType; import org.apache.commons.vfs.provider.AbstractFileObject; +import org.apache.commons.vfs.util.MonitorInputStream; +import org.apache.commons.vfs.util.MonitorOutputStream; /** * An FTP file. @@ -83,9 +85,6 @@ private FTPFile fileInfo; private FTPFile[] children; - // The client currently being used to read/write the content of this file - private FTPClient client; - public FtpFileObject( final FileName name, final FtpFileSystem fileSystem, final FileName rootName ) @@ -99,8 +98,14 @@ /** * Called by child file objects, to locate their ftp file info. */ - private FTPFile getChildFile( final String name ) throws Exception + private FTPFile getChildFile( final String name, final boolean flush ) + throws IOException { + if ( flush ) + { + children = null; + } + // List the children of this file doGetChildren(); @@ -148,13 +153,19 @@ * Attaches this file object to its file resource. */ protected void doAttach() - throws Exception + throws IOException { // Get the parent folder to find the info for this file + getInfo( false ); + } + + /** Fetches the info for this file. */ + private void getInfo( boolean flush ) throws IOException + { final FtpFileObject parent = (FtpFileObject)getParent(); if ( parent != null ) { - fileInfo = parent.getChildFile( getName().getBaseName() ); + fileInfo = parent.getChildFile( getName().getBaseName(), flush ); } if ( fileInfo == null || !fileInfo.isDirectory() ) { @@ -180,6 +191,15 @@ } /** + * Called when the type or content of this file changes. + */ + protected void onChange() throws IOException + { + children = null; + getInfo( true ); + } + + /** * Determines the type of the file, returns null if the file does not * exist. */ @@ -274,7 +294,6 @@ { throw new FileSystemException( "vfs.provider.ftp/create-folder.error", getName() ); } - detach(); } /** @@ -290,64 +309,90 @@ */ protected InputStream doGetInputStream() throws Exception { - client = ftpFs.getClient(); - return client.retrieveFileStream( relPath ); + final FTPClient client = ftpFs.getClient(); + final InputStream instr = client.retrieveFileStream( relPath ); + return new FtpInputStream( client, instr ); } /** - * Notification of the input stream being closed. + * Creates an output stream to write the file content to. */ - protected void doEndInput() + protected OutputStream doGetOutputStream() throws Exception { - final boolean ok; - try - { - ok = client.completePendingCommand(); - } - finally + final FTPClient client = ftpFs.getClient(); + return new FtpOutputStream( client, client.storeFileStream( relPath ) ); + } + + /** + * An InputStream that monitors for end-of-file. + */ + private class FtpInputStream + extends MonitorInputStream + { + private final FTPClient client; + + public FtpInputStream( final FTPClient client, final InputStream in ) { - ftpFs.putClient( client ); - client = null; + super( in ); + this.client = client; } - if ( !ok ) + /** + * Called after the stream has been closed. + */ + protected void onClose() throws IOException { - throw new FileSystemException( "vfs.provider.ftp/finish-get.error", getName() ); + final boolean ok; + try + { + ok = client.completePendingCommand(); + } + finally + { + ftpFs.putClient( client ); + } + + if ( !ok ) + { + throw new FileSystemException( "vfs.provider.ftp/finish-get.error", getName() ); + } } } /** - * Creates an output stream to write the file content to. + * An OutputStream that monitors for end-of-file. */ - protected OutputStream doGetOutputStream() - throws Exception + private class FtpOutputStream + extends MonitorOutputStream { - client = ftpFs.getClient(); - return client.storeFileStream( relPath ); - } + private final FTPClient client; - /** - * Notification of the output stream being closed. - */ - protected void doEndOutput() - throws Exception - { - final boolean ok; - try - { - ok = client.completePendingCommand(); - } - finally + public FtpOutputStream( final FTPClient client, final OutputStream outstr ) { - ftpFs.putClient( client ); - client = null; + super( outstr ); + this.client = client; } - if ( !ok ) + /** + * Called after this stream is closed. + */ + protected void onClose() throws IOException { - throw new FileSystemException( "vfs.provider.ftp/finish-put.error", getName() ); + final boolean ok; + try + { + ok = client.completePendingCommand(); + } + finally + { + ftpFs.putClient( client ); + } + + if ( !ok ) + { + throw new FileSystemException( "vfs.provider.ftp/finish-put.error", getName() ); + } } - detach(); } } 1.1 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/util/MonitorInputStream.java Index: MonitorInputStream.java =================================================================== /* ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.vfs.util; import java.io.BufferedInputStream; import java.io.InputStream; import java.io.IOException; /** * An InputStream that provides buffering and end-of-stream monitoring. * * @author <a href="mailto:[EMAIL PROTECTED]">Adam Murdoch</a> * @version $Revision: 1.1 $ $Date: 2003/02/13 04:28:45 $ */ public class MonitorInputStream extends BufferedInputStream { private boolean finished; public MonitorInputStream( final InputStream in ) { super( in ); } /** * Reads a character. */ public int read() throws IOException { if ( finished ) { return -1; } final int ch = super.read(); if ( ch != -1 ) { return ch; } // End-of-stream close(); return -1; } /** * Reads bytes from this input stream.error occurs. */ public int read( final byte[] buffer, final int offset, final int length ) throws IOException { if ( finished ) { return -1; } final int nread = super.read( buffer, offset, length ); if ( nread != -1 ) { return nread; } // End-of-stream close(); return -1; } /** * Closes this input stream and releases any system resources * associated with the stream. */ public void close() throws IOException { if ( finished ) { return; } // Close the stream IOException exc = null; try { super.close(); } catch ( final IOException ioe ) { exc = ioe; } // Notify that the stream has been closed try { onClose(); } catch ( final IOException ioe ) { exc = ioe; } finished = true; if ( exc != null ) { throw exc; } } /** * Called after the stream has been closed. This implementation does * nothing. */ protected void onClose() throws IOException { } } 1.1 jakarta-commons-sandbox/vfs/src/java/org/apache/commons/vfs/util/MonitorOutputStream.java Index: MonitorOutputStream.java =================================================================== /* ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.vfs.util; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; /** * An OutputStream that provides buffering and end-of-stream monitoring. * * @author <a href="mailto:[EMAIL PROTECTED]">Adam Murdoch</a> * @version $Revision: 1.1 $ $Date: 2003/02/13 04:28:45 $ */ public class MonitorOutputStream extends BufferedOutputStream { private boolean finished; public MonitorOutputStream( final OutputStream out ) { super( out ); } /** * Closes this output stream. */ public void close() throws IOException { if ( finished ) { return; } // Close the output stream IOException exc = null; try { super.close(); } catch ( final IOException ioe ) { exc = ioe; } // Notify of end of output try { onClose(); } catch ( final IOException ioe ) { exc = ioe; } finished = true; if ( exc != null ) { throw exc; } } /** * Called after this stream is closed. This implementation does nothing. */ protected void onClose() throws IOException { } } 1.3 +3 -3 jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/provider/test/JunctionTests.java Index: JunctionTests.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/provider/test/JunctionTests.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- JunctionTests.java 12 Feb 2003 07:56:19 -0000 1.2 +++ JunctionTests.java 13 Feb 2003 04:28:46 -0000 1.3 @@ -94,7 +94,7 @@ } catch ( final Exception e ) { - assertSameMessage( "impl/nested-junction.error", "vfs:/a/b", e ); + assertSameMessage( "vfs.impl/nested-junction.error", "vfs:/a/b", e ); } // At same point @@ -105,7 +105,7 @@ } catch ( final Exception e ) { - assertSameMessage( "impl/nested-junction.error", "vfs:/a", e ); + assertSameMessage( "vfs.impl/nested-junction.error", "vfs:/a", e ); } } 1.3 +32 -17 jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/test/ContentTests.java Index: ContentTests.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/test/ContentTests.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- ContentTests.java 12 Feb 2003 07:56:19 -0000 1.2 +++ ContentTests.java 13 Feb 2003 04:28:46 -0000 1.3 @@ -55,14 +55,14 @@ */ package org.apache.commons.vfs.test; +import java.io.InputStream; +import java.util.Iterator; +import org.apache.commons.vfs.FileContent; import org.apache.commons.vfs.FileObject; -import org.apache.commons.vfs.NameScope; -import org.apache.commons.vfs.FileType; -import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileSystem; -import org.apache.commons.vfs.FileContent; -import java.util.Iterator; -import java.io.InputStream; +import org.apache.commons.vfs.FileSystemException; +import org.apache.commons.vfs.FileType; +import org.apache.commons.vfs.NameScope; /** * Test cases for reading file content. @@ -254,9 +254,7 @@ } /** - * Tests concurrent reads on a file fail. - * - * @todo Change model so that concurrent reads are allowed (maybe optional) + * Tests concurrent reads on a file. */ public void testConcurrentRead() throws Exception { @@ -268,12 +266,7 @@ try { // Start reading again - file.getContent().getInputStream(); - fail(); - } - catch ( final Exception e ) - { - assertSameMessage( "vfs.provider/read-in-use.error", file, e ); + file.getContent().getInputStream().close(); } finally { @@ -326,5 +319,27 @@ // Read the content again assertSameContent( FILE1_CONTENT, file ); } - + + /** + * Tests that input streams are cleaned up on file close. + */ + public void testInstrCleanup() throws Exception + { + // Get the test file + FileObject file = getReadFolder().resolveFile( "file1.txt" ); + assertEquals( FileType.FILE, file.getType() ); + + // Open some input streams + final InputStream instr1 = file.getContent().getInputStream(); + assertTrue( instr1.read() == FILE1_CONTENT.charAt( 0 ) ); + final InputStream instr2 = file.getContent().getInputStream(); + assertTrue( instr2.read() == FILE1_CONTENT.charAt( 0 ) ); + + // Close the file + file.close(); + + // Check + assertTrue( instr1.read() == -1 ); + assertTrue( instr2.read() == -1 ); + } } 1.9 +41 -3 jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/test/ProviderWriteTests.java Index: ProviderWriteTests.java =================================================================== RCS file: /home/cvs/jakarta-commons-sandbox/vfs/src/test/org/apache/commons/vfs/test/ProviderWriteTests.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- ProviderWriteTests.java 12 Feb 2003 07:56:19 -0000 1.8 +++ ProviderWriteTests.java 13 Feb 2003 04:28:46 -0000 1.9 @@ -288,7 +288,43 @@ instr.close(); } } - + + /** + * Tests concurrent writes on the same file fails. + */ + public void testConcurrentWrite() throws Exception + { + final FileObject scratchFolder = createScratchFolder(); + + final FileObject file = scratchFolder.resolveFile( "file1.txt" ); + file.createFile(); + + // Start writing to the file + final OutputStream outstr = file.getContent().getOutputStream(); + final String testContent = "some content"; + try + { + // Write some content to the first stream + outstr.write( testContent.getBytes() ); + + // Try to open another output stream + file.getContent().getOutputStream(); + fail(); + } + catch ( final FileSystemException e ) + { + // Check error message + assertSameMessage( "vfs.provider/write-in-use.error", file, e ); + } + finally + { + outstr.close(); + } + + // Make sure that the content written to the first stream is actually applied + assertSameContent( testContent, file ); + } + /** * Tests file copy to and from the same filesystem type. This was a problem * w/ FTP. @@ -468,7 +504,8 @@ */ public void fileCreated( final FileChangeEvent event ) { - assertTrue( events.size() > 0 && events.remove( 0 ) == CREATE ); + assertTrue( "Unexpected create event", events.size() > 0 ); + assertSame( "Expecting a create event", CREATE, events.remove( 0 ) ); assertSame( file, event.getFile() ); try { @@ -485,7 +522,8 @@ */ public void fileDeleted( final FileChangeEvent event ) { - assertTrue( events.size() > 0 && events.remove( 0 ) == DELETE ); + assertTrue( "Unexpected delete event", events.size() > 0 ); + assertSame( "Expecting a delete event", DELETE, events.remove( 0 ) ); assertSame( file, event.getFile() ); try {
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]