* Added FileObject.copy(). * Renamed <v-copy> task attributes file -> srcfile, tofile -> destfile, todir -> destdir. * Moved provider instantiation out of DefaultFileSystemManager, and into myrmidon-aware VfsManager. Providers are instantiated using the TypeManager. The list of providers isn't configurable yet. * Some support for %nn encoded URI (not quite complete). * Zip file system now handles zip files from any file system, not just local files. Still read-only at this stage. Uses a truely dodgy and very temporary replication mechanism. * Zip file system now handles relative paths in URI (e.g. zip:relpath.zip), that are resolved against the base dir. * Fixed bug in resolving names against the root file of a file system. * Changed behaviour of FileName.resolveName( ".." ) for the root file of a file system. * Added more test cases. * A bucketload of other minor changes. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271803 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -557,6 +557,9 @@ Legal: | |||
| <zip zipfile="${test.vfs.dir}/test.zip"> | |||
| <fileset dir="${test.vfs.dir}" includes="basedir/**"/> | |||
| </zip> | |||
| <zip zipfile="${test.vfs.dir}/nested.zip"> | |||
| <fileset dir="${test.vfs.dir}" includes="test.zip"/> | |||
| </zip> | |||
| <!-- Prepare deployer tests --> | |||
| <property name="test.deployer.dir" | |||
| @@ -117,15 +117,22 @@ | |||
| <blockquote> | |||
| <p>The VFS needs plenty of work:</p> | |||
| <ul> | |||
| <li>Move and copy files/folders.</li> | |||
| <li>Move files/folders.</li> | |||
| <li>Recursive folders copy.</li> | |||
| <li>Search through a file hierarchy, using Ant-style wildcards.</li> | |||
| <li>Search through a file hierarchy, using a Selector interface.</li> | |||
| <li>The in-memory caching mechanism is pretty rudimentary at this stage. | |||
| It needs work to make it size capped. In addition, some mechanism needs | |||
| to be provided to release and refresh cached info. | |||
| </li> | |||
| <li>Convert files/folders into local files, for handing off | |||
| to external commands, or legacy tasks.</li> | |||
| <li>Refactor the replication mechanism out of ZipFileSystemProvder, | |||
| and make more general pluggable.</li> | |||
| <li>Capabilities discovery.</li> | |||
| <li>Attributes and attribute schema.</li> | |||
| <li>Handle file canonicalisation better (for cases like case-insensitive | |||
| file systems, symbolic links, name encoding, etc).</li> | |||
| <li>File system layering. That is, the ability for a file system to | |||
| sit on top of another file system, or a file from another file system | |||
| (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | |||
| @@ -300,10 +307,6 @@ | |||
| <p>A completely unordered list of items, big and small:</p> | |||
| <ul> | |||
| <li>Search through the code for 'TODO' items and fix them.</li> | |||
| <li>Tidy-up CLIMain so that it calls System.exit() with a non-zero exit code, | |||
| if the build fails.</li> | |||
| <li>Tidy-up the 'build failed' message, so that the stack trace is only | |||
| printed out if the log level is verbose/debug.</li> | |||
| <li>Allow service factories to be configured from the contents of the | |||
| <code>ant-services.xml</code> descriptor.</li> | |||
| <li>Route external process stdout and stderr through the logger.</li> | |||
| @@ -314,11 +317,10 @@ | |||
| <li>Fire ProjectListener events projectStarted() and projectFinished() | |||
| events on start and finish of referenced projects, adding indicator methods | |||
| to ProjectEvent.</li> | |||
| <li>Convert PropertyUtil to a non-static PropertyResolver service.</li> | |||
| <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | |||
| names like "," or "", or " ". Probably want to exclude names that start or | |||
| names like "," or "", or " ". Probably want to reject names that start or | |||
| end with white-space (though internal whitespace is probably fine). We also | |||
| want to reserve certain punctuation characters like . , : ? [ ] { }, etc for | |||
| want to reserve certain punctuation characters like , : ? $ [ ] { } < >, etc for | |||
| future use.</li> | |||
| <li>Similarly, validate property names, using the same rules.</li> | |||
| <li>Detect duplicate type names.</li> | |||
| @@ -330,6 +332,7 @@ | |||
| an antlib.</li> | |||
| <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | |||
| <li>Allow the <code><if></code> task to take any condition implementation.</li> | |||
| <li>Add an else block to the <code><if></code> task.</li> | |||
| <li>Unit tests.</li> | |||
| </ul> | |||
| </blockquote> | |||
| @@ -125,7 +125,8 @@ files are found in the <code>lib</code> directory:</p> | |||
| <td bgcolor="#a0ddf0" colspan="" rowspan="" | |||
| valign="top" align="left"> | |||
| <font color="#000000" size="-1" face="arial,helvetica,sanserif"> | |||
| <a href="http://jcifs.samba.org">jcifs.samba.org</a> | |||
| <a href="http://jcifs.samba.org">jcifs.samba.org</a>. | |||
| <p>Note: there are problems using the 0.6.1 release. Try 0.6.0 instead.</p> | |||
| </font> | |||
| </td> | |||
| </tr> | |||
| @@ -1,7 +1,7 @@ | |||
| <project version="2.0"> | |||
| <target name="copy"> | |||
| <v-fileset id="src-files" dir="src"/> | |||
| <v-copy todir="dest"> | |||
| <v-copy destdir="dest"> | |||
| <v-fileset-ref id="src-files"/> | |||
| </v-copy> | |||
| </target> | |||
| @@ -7,8 +7,6 @@ | |||
| */ | |||
| package org.apache.antlib.vfile; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import java.util.ArrayList; | |||
| import java.util.Iterator; | |||
| import org.apache.aut.vfs.FileObject; | |||
| @@ -41,7 +39,7 @@ public class CopyFilesTask | |||
| /** | |||
| * Sets the source file. | |||
| */ | |||
| public void setFile( final FileObject file ) | |||
| public void setSrcfile( final FileObject file ) | |||
| { | |||
| m_srcFile = file; | |||
| } | |||
| @@ -49,7 +47,7 @@ public class CopyFilesTask | |||
| /** | |||
| * Sets the destination file. | |||
| */ | |||
| public void setTofile( final FileObject file ) | |||
| public void setDestfile( final FileObject file ) | |||
| { | |||
| m_destFile = file; | |||
| } | |||
| @@ -57,11 +55,19 @@ public class CopyFilesTask | |||
| /** | |||
| * Sets the destination directory. | |||
| */ | |||
| public void setTodir( final FileObject file ) | |||
| public void setDestdir( final FileObject file ) | |||
| { | |||
| m_destDir = file; | |||
| } | |||
| /** | |||
| * Sets the source directory. | |||
| */ | |||
| public void setSrcdir( final FileObject dir ) | |||
| { | |||
| add( new DefaultFileSet( dir ) ); | |||
| } | |||
| /** | |||
| * Adds a source file set. | |||
| */ | |||
| @@ -107,7 +113,8 @@ public class CopyFilesTask | |||
| m_destFile = m_destDir.resolveFile( m_srcFile.getName().getBaseName() ); | |||
| } | |||
| copyFile( m_srcFile, m_destFile ); | |||
| getLogger().info( "copy " + m_srcFile + " to " + m_destFile ); | |||
| m_destFile.copy( m_srcFile ); | |||
| } | |||
| // Copy the contents of the filesets across | |||
| @@ -134,7 +141,8 @@ public class CopyFilesTask | |||
| final FileObject destFile = m_destDir.resolveFile( path, NameScope.DESCENDENT ); | |||
| // Copy the file across | |||
| copyFile( srcFile, destFile ); | |||
| getLogger().info( "copy " + srcFile + " to " + destFile ); | |||
| destFile.copy( srcFile ); | |||
| } | |||
| } | |||
| } | |||
| @@ -144,42 +152,4 @@ public class CopyFilesTask | |||
| } | |||
| } | |||
| /** | |||
| * Copies a file. | |||
| */ | |||
| private void copyFile( final FileObject srcFile, final FileObject destFile ) | |||
| throws TaskException | |||
| { | |||
| getLogger().info( "copy " + srcFile + " to " + destFile ); | |||
| try | |||
| { | |||
| // TODO - move copy behind FileObject interface | |||
| InputStream instr = srcFile.getContent().getInputStream(); | |||
| try | |||
| { | |||
| OutputStream outstr = destFile.getContent().getOutputStream(); | |||
| byte[] buffer = new byte[ 4096 ]; | |||
| while( true ) | |||
| { | |||
| int nread = instr.read( buffer ); | |||
| if( nread == -1 ) | |||
| { | |||
| break; | |||
| } | |||
| outstr.write( buffer, 0, nread ); | |||
| } | |||
| outstr.close(); | |||
| } | |||
| finally | |||
| { | |||
| instr.close(); | |||
| } | |||
| } | |||
| catch( Exception exc ) | |||
| { | |||
| final String message = REZ.getString( "copyfilestask.copy-file.error", srcFile, destFile ); | |||
| throw new TaskException( message, exc ); | |||
| } | |||
| } | |||
| } | |||
| @@ -36,6 +36,15 @@ public class DefaultFileSet | |||
| private FileObject m_dir; | |||
| private final AndFileSelector m_selector = new AndFileSelector(); | |||
| public DefaultFileSet() | |||
| { | |||
| } | |||
| public DefaultFileSet( final FileObject dir ) | |||
| { | |||
| m_dir = dir; | |||
| } | |||
| /** | |||
| * Sets the root directory. | |||
| */ | |||
| @@ -7,6 +7,7 @@ | |||
| */ | |||
| package org.apache.antlib.vfile; | |||
| import java.util.ArrayList; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.myrmidon.api.AbstractTask; | |||
| import org.apache.myrmidon.api.TaskException; | |||
| @@ -22,11 +23,11 @@ import org.apache.myrmidon.api.TaskException; | |||
| public class ListFileSetTask | |||
| extends AbstractTask | |||
| { | |||
| private FileSet m_fileSet; | |||
| private final ArrayList m_fileSets = new ArrayList(); | |||
| public void set( final FileSet fileSet ) | |||
| public void add( final FileSet fileSet ) | |||
| { | |||
| m_fileSet = fileSet; | |||
| m_fileSets.add( fileSet ); | |||
| } | |||
| /** | |||
| @@ -35,14 +36,19 @@ public class ListFileSetTask | |||
| public void execute() | |||
| throws TaskException | |||
| { | |||
| FileSetResult result = m_fileSet.getResult( getContext() ); | |||
| final FileObject[] files = result.getFiles(); | |||
| final String[] paths = result.getPaths(); | |||
| for( int i = 0; i < files.length; i++ ) | |||
| final int count = m_fileSets.size(); | |||
| for( int i = 0; i < count; i++ ) | |||
| { | |||
| final FileObject file = files[ i ]; | |||
| final String path = paths[ i ]; | |||
| getLogger().info( path + " = " + file ); | |||
| final FileSet fileSet = (FileSet)m_fileSets.get(i ); | |||
| FileSetResult result = fileSet.getResult( getContext() ); | |||
| final FileObject[] files = result.getFiles(); | |||
| final String[] paths = result.getPaths(); | |||
| for( int j = 0; j < files.length; j++ ) | |||
| { | |||
| final FileObject file = files[ j ]; | |||
| final String path = paths[ j ]; | |||
| getLogger().info( path + " = " + file ); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -6,6 +6,5 @@ fileset.list-files.error=Could not list the files in folder "{0}". | |||
| copyfilestask.no-source.error=No source files specified for {0} task. | |||
| copyfilestask.no-destination.error=No destination file or directory specified for {0} task. | |||
| copyfilestask.no-destination.error=No destination directory specified for {0} task. | |||
| copyfilestask.copy-file.error=Could not copy "{0}" to "{1}". | |||
| filteredfilelist.no-selector.error=No filter criteria specified. | |||
| @@ -181,6 +181,20 @@ public interface FileObject | |||
| */ | |||
| void create( FileType type ) throws FileSystemException; | |||
| /** | |||
| * Copies the content of another file to this file. | |||
| * | |||
| * If this file does not exist, it is created. Its parent folder is also | |||
| * created, if necessary. If this file does exist, its content is replaced. | |||
| * | |||
| * @param file The file to copy the content from. | |||
| * | |||
| * @throws FileSystemException | |||
| * If this file is read-only, or is a folder, or if the supplied file | |||
| * is not a file, or on error copying the content. | |||
| */ | |||
| void copy( FileObject file ) throws FileSystemException; | |||
| /** | |||
| * Returns this file's content. The {@link FileContent} returned by this | |||
| * method can be used to read and write the content of the file. | |||
| @@ -189,6 +203,12 @@ public interface FileObject | |||
| * the returned {@link FileContent} can be used to create the file | |||
| * by writing its content. | |||
| * | |||
| * @todo Do not throw an exception if this file is a folder. Instead, | |||
| * throw the exceptions when (if) any methods on the returned object | |||
| * are called. This is to hand 2 cases: when the folder is deleted | |||
| * and recreated as a file, and to allow attributes of the folder | |||
| * to be set (last modified time, permissions, etc). | |||
| * | |||
| * @return | |||
| * This file's content. | |||
| * | |||
| @@ -71,7 +71,8 @@ public interface FileSystemManager | |||
| * @throws FileSystemException | |||
| * On error parsing the file name. | |||
| */ | |||
| FileObject resolveFile( String name ) throws FileSystemException; | |||
| FileObject resolveFile( String name ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Locates a file by name. The name is resolved as described | |||
| @@ -90,7 +91,8 @@ public interface FileSystemManager | |||
| * @throws FileSystemException | |||
| * On error parsing the file name. | |||
| */ | |||
| FileObject resolveFile( FileObject baseFile, String name ) throws FileSystemException; | |||
| FileObject resolveFile( FileObject baseFile, String name ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Locates a file by name. See {@link #resolveFile(FileObject, String)} | |||
| @@ -106,5 +108,39 @@ public interface FileSystemManager | |||
| * On error parsing the file name. | |||
| * | |||
| */ | |||
| FileObject resolveFile( File baseFile, String name ) throws FileSystemException; | |||
| FileObject resolveFile( File baseFile, String name ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Converts a local file into a {@link FileObject}. | |||
| * | |||
| * @param file | |||
| * The file to convert. | |||
| * | |||
| * @return | |||
| * The {@link FileObject} that represents the local file. | |||
| * | |||
| * @throws FileSystemException | |||
| * On error converting the file. | |||
| */ | |||
| FileObject convert( File file ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Creates a layered file system. A layered file system is a file system | |||
| * that is created from the contents of another file file, such as a zip | |||
| * or tar file. | |||
| * | |||
| * @param provider | |||
| * The name of the file system provider to use. This name is | |||
| * the same as the scheme used in URI to identify the provider. | |||
| * | |||
| * @param file | |||
| * The file to use to create the file system. | |||
| * | |||
| * @throws FileSystemException | |||
| * On error creating the file system. | |||
| */ | |||
| FileObject createFileSystem( String provider, FileObject file ) | |||
| throws FileSystemException; | |||
| } | |||
| @@ -9,14 +9,11 @@ package org.apache.aut.vfs.impl; | |||
| import java.io.File; | |||
| import java.util.HashMap; | |||
| import java.util.Iterator; | |||
| import java.util.Map; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.FileSystemManager; | |||
| import org.apache.aut.vfs.provider.FileSystem; | |||
| import org.apache.aut.vfs.provider.FileSystemProvider; | |||
| import org.apache.aut.vfs.provider.FileSystemProviderContext; | |||
| import org.apache.aut.vfs.provider.UriParser; | |||
| import org.apache.aut.vfs.provider.local.LocalFileSystemProvider; | |||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | |||
| @@ -25,7 +22,8 @@ import org.apache.avalon.excalibur.i18n.Resources; | |||
| /** | |||
| * A default file system manager implementation. | |||
| * | |||
| * @author Adam Murdoch | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| * @version $Revision$ $Date$ | |||
| */ | |||
| public class DefaultFileSystemManager | |||
| implements FileSystemManager | |||
| @@ -40,69 +38,55 @@ public class DefaultFileSystemManager | |||
| private final Map m_providers = new HashMap(); | |||
| /** The provider context. */ | |||
| private final ProviderContextImpl m_context = new ProviderContextImpl(); | |||
| private final DefaultProviderContext m_context = new DefaultProviderContext( this ); | |||
| /** The base file to use for relative URI. */ | |||
| private FileObject m_baseFile; | |||
| /** | |||
| * The cached file systems. This is a mapping from root URI to | |||
| * FileSystem object. | |||
| */ | |||
| private final Map m_fileSystems = new HashMap(); | |||
| public DefaultFileSystemManager() throws Exception | |||
| public DefaultFileSystemManager() | |||
| { | |||
| // Create the local provider | |||
| m_localFileProvider = new LocalFileSystemProvider(); | |||
| m_providers.put( "file", m_localFileProvider ); | |||
| m_localFileProvider.setContext( m_context ); | |||
| } | |||
| // TODO - make this list configurable | |||
| // Create the providers | |||
| FileSystemProvider provider = createProvider( "org.apache.aut.vfs.provider.zip.ZipFileSystemProvider" ); | |||
| if( provider != null ) | |||
| { | |||
| m_providers.put( "zip", provider ); | |||
| m_providers.put( "jar", provider ); | |||
| } | |||
| provider = createProvider( "org.apache.aut.vfs.provider.smb.SmbFileSystemProvider" ); | |||
| if( provider != null ) | |||
| { | |||
| m_providers.put( "smb", provider ); | |||
| } | |||
| provider = createProvider( "org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider" ); | |||
| if( provider != null ) | |||
| { | |||
| m_providers.put( "ftp", provider ); | |||
| } | |||
| // Contextualise the providers | |||
| for( Iterator iterator = m_providers.values().iterator(); iterator.hasNext(); ) | |||
| { | |||
| provider = (FileSystemProvider)iterator.next(); | |||
| provider.setContext( m_context ); | |||
| } | |||
| /** | |||
| * Registers a file system provider. | |||
| */ | |||
| public void addProvider( final String urlScheme, | |||
| final FileSystemProvider provider ) | |||
| throws FileSystemException | |||
| { | |||
| addProvider( new String[] { urlScheme }, provider ); | |||
| } | |||
| /** | |||
| * Creates a provider instance, returns null if the provider class is | |||
| * not found. | |||
| * Registers a file system provider. | |||
| */ | |||
| private FileSystemProvider createProvider( final String className ) | |||
| throws Exception | |||
| public void addProvider( final String[] urlSchemes, | |||
| final FileSystemProvider provider ) | |||
| throws FileSystemException | |||
| { | |||
| try | |||
| // Check for duplicates | |||
| for( int i = 0; i < urlSchemes.length; i++ ) | |||
| { | |||
| // TODO - wrap exceptions | |||
| return (FileSystemProvider)Class.forName( className ).newInstance(); | |||
| final String scheme = urlSchemes[i ]; | |||
| if( m_providers.containsKey( scheme ) ) | |||
| { | |||
| final String message = REZ.getString( "multiple-providers-for-scheme.error", scheme ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| } | |||
| catch( ClassNotFoundException e ) | |||
| // Contextualise | |||
| provider.setContext( m_context ); | |||
| // Add to map | |||
| for( int i = 0; i < urlSchemes.length; i++ ) | |||
| { | |||
| // This is fine, for now | |||
| return null; | |||
| final String scheme = urlSchemes[ i ]; | |||
| m_providers.put( scheme, provider ); | |||
| } | |||
| } | |||
| @@ -152,7 +136,7 @@ public class DefaultFileSystemManager | |||
| public FileObject resolveFile( final File baseFile, final String uri ) | |||
| throws FileSystemException | |||
| { | |||
| final FileObject baseFileObj = m_localFileProvider.findFileByLocalName( baseFile ); | |||
| final FileObject baseFileObj = m_localFileProvider.findLocalFile( baseFile ); | |||
| return resolveFile( baseFileObj, uri ); | |||
| } | |||
| @@ -170,14 +154,17 @@ public class DefaultFileSystemManager | |||
| final FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | |||
| if( provider != null ) | |||
| { | |||
| return provider.findFile( uri ); | |||
| return provider.findFile( baseFile, uri ); | |||
| } | |||
| } | |||
| // Decode the URI (remove %nn encodings) | |||
| final String decodedUri = UriParser.decode( uri ); | |||
| // Handle absolute file names | |||
| if( m_localFileProvider.isAbsoluteLocalName( uri ) ) | |||
| if( m_localFileProvider.isAbsoluteLocalName( decodedUri ) ) | |||
| { | |||
| return m_localFileProvider.findLocalFile( uri ); | |||
| return m_localFileProvider.findLocalFile( decodedUri ); | |||
| } | |||
| // Assume a bad scheme | |||
| @@ -193,32 +180,31 @@ public class DefaultFileSystemManager | |||
| final String message = REZ.getString( "find-rel-file.error", uri ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| return baseFile.resolveFile( uri ); | |||
| return baseFile.resolveFile( decodedUri ); | |||
| } | |||
| /** | |||
| * A provider context implementation. | |||
| * Converts a local file into a {@link FileObject}. | |||
| */ | |||
| private final class ProviderContextImpl | |||
| implements FileSystemProviderContext | |||
| public FileObject convert( final File file ) | |||
| throws FileSystemException | |||
| { | |||
| /** | |||
| * Locates a cached file system by root URI. | |||
| */ | |||
| public FileSystem getFileSystem( final String rootURI ) | |||
| { | |||
| // TODO - need to have a per-fs uri comparator | |||
| return (FileSystem)m_fileSystems.get( rootURI ); | |||
| } | |||
| return m_localFileProvider.findLocalFile( file ); | |||
| } | |||
| /** | |||
| * Registers a file system for caching. | |||
| */ | |||
| public void putFileSystem( final String rootURI, final FileSystem fs ) | |||
| throws FileSystemException | |||
| /** | |||
| * Creates a layered file system. | |||
| */ | |||
| public FileObject createFileSystem( final String scheme, | |||
| final FileObject file ) | |||
| throws FileSystemException | |||
| { | |||
| FileSystemProvider provider = (FileSystemProvider)m_providers.get( scheme ); | |||
| if( provider == null ) | |||
| { | |||
| // TODO - should really check that there's not one already cached | |||
| m_fileSystems.put( rootURI, fs ); | |||
| final String message = REZ.getString( "unknown-provider.error", scheme ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| return provider.createFileSystem( scheme, file ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,66 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.aut.vfs.impl; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.FileSystem; | |||
| import org.apache.aut.vfs.provider.FileSystemProviderContext; | |||
| /** | |||
| * A provider context implementation. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| * @version $Revision$ $Date$ | |||
| */ | |||
| final class DefaultProviderContext | |||
| implements FileSystemProviderContext | |||
| { | |||
| private final DefaultFileSystemManager m_manager; | |||
| /** | |||
| * The cached file systems. This is a mapping from root URI to | |||
| * FileSystem object. | |||
| */ | |||
| private final Map m_fileSystems = new HashMap(); | |||
| public DefaultProviderContext( final DefaultFileSystemManager manager ) | |||
| { | |||
| m_manager = manager; | |||
| } | |||
| /** | |||
| * Locate a file by name. | |||
| */ | |||
| public FileObject resolveFile( final FileObject baseFile, final String name ) | |||
| throws FileSystemException | |||
| { | |||
| return m_manager.resolveFile( baseFile, name ); | |||
| } | |||
| /** | |||
| * Locates a cached file system by root URI. | |||
| */ | |||
| public FileSystem getFileSystem( final String rootURI ) | |||
| { | |||
| // TODO - need to have a per-fs uri comparator | |||
| return (FileSystem)m_fileSystems.get( rootURI ); | |||
| } | |||
| /** | |||
| * Registers a file system for caching. | |||
| */ | |||
| public void putFileSystem( final String rootURI, final FileSystem fs ) | |||
| throws FileSystemException | |||
| { | |||
| // TODO - should really check that there's not one already cached | |||
| m_fileSystems.put( rootURI, fs ); | |||
| } | |||
| } | |||
| @@ -1,3 +1,5 @@ | |||
| # DefaultFileSystemManager | |||
| unknown-scheme.error=Unknown scheme "{0}" in URI "{1}". | |||
| find-rel-file.error=Could not find file with URI "{0}" because it is a relative path, and no base URI was provided. | |||
| multiple-providers-for-scheme.error=Multiple file system providers registered for URL scheme "{0}". | |||
| unknown-provider.error=Unknown file system provider "{0}". | |||
| @@ -21,6 +21,7 @@ import org.apache.aut.vfs.FileType; | |||
| import org.apache.aut.vfs.NameScope; | |||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | |||
| import org.apache.avalon.excalibur.i18n.Resources; | |||
| import org.apache.avalon.excalibur.io.IOUtil; | |||
| /** | |||
| * A partial file object implementation. | |||
| @@ -349,7 +350,7 @@ public abstract class AbstractFileObject | |||
| } | |||
| // Update cached info | |||
| updateType( null ); | |||
| updateType(); | |||
| } | |||
| /** | |||
| @@ -463,7 +464,41 @@ public abstract class AbstractFileObject | |||
| } | |||
| // Update cached info | |||
| updateType( type ); | |||
| updateType(); | |||
| } | |||
| /** | |||
| * Copies the content of another file to this file. | |||
| */ | |||
| public void copy( final FileObject file ) throws FileSystemException | |||
| { | |||
| try | |||
| { | |||
| final InputStream instr = file.getContent().getInputStream(); | |||
| try | |||
| { | |||
| // Create the output strea via getContent(), to pick up the | |||
| // validation it does | |||
| final OutputStream outstr = getContent().getOutputStream(); | |||
| try | |||
| { | |||
| IOUtil.copy( instr, outstr ); | |||
| } | |||
| finally | |||
| { | |||
| IOUtil.shutdownStream( outstr ); | |||
| } | |||
| } | |||
| finally | |||
| { | |||
| IOUtil.shutdownStream( instr ); | |||
| } | |||
| } | |||
| catch( final Exception exc ) | |||
| { | |||
| final String message = REZ.getString( "copy-file.error", file.getName(), m_name ); | |||
| throw new FileSystemException( message, exc ); | |||
| } | |||
| } | |||
| /** | |||
| @@ -598,14 +633,14 @@ public abstract class AbstractFileObject | |||
| */ | |||
| public void endOutput() throws Exception | |||
| { | |||
| updateType( FileType.FILE ); | |||
| updateType(); | |||
| doEndOutput(); | |||
| } | |||
| /** | |||
| * Update cached info when this file's type changes. | |||
| */ | |||
| private void updateType( FileType type ) | |||
| private void updateType() | |||
| { | |||
| // Notify parent that its child list may no longer be valid | |||
| notifyParent(); | |||
| @@ -23,13 +23,21 @@ public abstract class AbstractFileSystemProvider | |||
| private final static Resources REZ = | |||
| ResourceManager.getPackageResources( AbstractFileSystemProvider.class ); | |||
| protected FileSystemProviderContext m_context; | |||
| private FileSystemProviderContext m_context; | |||
| /** | |||
| * Returns the context for this provider. | |||
| */ | |||
| protected FileSystemProviderContext getContext() | |||
| { | |||
| return m_context; | |||
| } | |||
| /** | |||
| * Sets the context for this file system provider. This method is called | |||
| * before any of the other provider methods. | |||
| */ | |||
| public void setContext( FileSystemProviderContext context ) | |||
| public void setContext( final FileSystemProviderContext context ) | |||
| { | |||
| m_context = context; | |||
| } | |||
| @@ -40,13 +48,14 @@ public abstract class AbstractFileSystemProvider | |||
| * @param uri | |||
| * The absolute URI of the file to find. | |||
| */ | |||
| public FileObject findFile( String uri ) throws FileSystemException | |||
| public FileObject findFile( final FileObject baseFile, | |||
| final String uri ) throws FileSystemException | |||
| { | |||
| // Parse the URI | |||
| ParsedUri parsedURI = null; | |||
| ParsedUri parsedUri = null; | |||
| try | |||
| { | |||
| parsedURI = parseURI( uri ); | |||
| parsedUri = parseUri( baseFile, uri ); | |||
| } | |||
| catch( FileSystemException exc ) | |||
| { | |||
| @@ -54,31 +63,70 @@ public abstract class AbstractFileSystemProvider | |||
| throw new FileSystemException( message, exc ); | |||
| } | |||
| // Locate the file | |||
| return findFile( parsedUri ); | |||
| } | |||
| /** | |||
| * Locates a file from its parsed URI. | |||
| */ | |||
| private FileObject findFile( final ParsedUri parsedUri ) | |||
| throws FileSystemException | |||
| { | |||
| // Check in the cache for the file system | |||
| FileSystem fs = m_context.getFileSystem( parsedURI.getRootURI() ); | |||
| final String rootUri = parsedUri.getRootUri(); | |||
| FileSystem fs = m_context.getFileSystem( rootUri ); | |||
| if( fs == null ) | |||
| { | |||
| // Need to create the file system | |||
| fs = createFileSystem( parsedURI ); | |||
| m_context.putFileSystem( parsedURI.getRootURI(), fs ); | |||
| // Need to create the file system, and cache it | |||
| fs = createFileSystem( parsedUri ); | |||
| m_context.putFileSystem( rootUri, fs ); | |||
| } | |||
| // Locate the file | |||
| return fs.findFile( parsedURI.getPath() ); | |||
| return fs.findFile( parsedUri.getPath() ); | |||
| } | |||
| /** | |||
| * Creates a layered file system. | |||
| */ | |||
| public FileObject createFileSystem( final String scheme, final FileObject file ) | |||
| throws FileSystemException | |||
| { | |||
| // TODO - this is a pretty shonky model for layered FS; need to revise | |||
| // Build the URI | |||
| final ParsedUri uri = buildUri( scheme, file ); | |||
| // Locate the file | |||
| return findFile( uri ); | |||
| } | |||
| /** | |||
| * Parses a URI into its components. The returned value is used to | |||
| * locate the file system in the cache (using the root prefix), and is | |||
| * passed to {@link #createFileSystem} to create the file system. | |||
| * locate the file system in the cache (using the root prefix). | |||
| * | |||
| * <p>The provider can annotate this object with any additional | |||
| * information it requires to create a file system from the URI. | |||
| */ | |||
| protected abstract ParsedUri parseURI( String uri ) throws FileSystemException; | |||
| protected abstract ParsedUri parseUri( final FileObject baseFile, final String uri ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Builds the URI for the root of a layered file system. | |||
| */ | |||
| protected ParsedUri buildUri( final String scheme, | |||
| final FileObject file ) | |||
| throws FileSystemException | |||
| { | |||
| final String message = REZ.getString( "not-layered-fs.error" ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| /** | |||
| * Creates the filesystem. | |||
| */ | |||
| protected abstract org.apache.aut.vfs.provider.FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException; | |||
| protected abstract FileSystem createFileSystem( final ParsedUri uri ) | |||
| throws FileSystemException; | |||
| } | |||
| @@ -12,6 +12,11 @@ import org.apache.aut.vfs.FileSystemException; | |||
| /** | |||
| * A file system provider, or factory. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| * @version $Revision$ $Date$ | |||
| * | |||
| * @ant:role shorthand="file-system" | |||
| */ | |||
| public interface FileSystemProvider | |||
| { | |||
| @@ -24,8 +29,16 @@ public interface FileSystemProvider | |||
| /** | |||
| * Locates a file object, by absolute URI. | |||
| * | |||
| * @param baseFile | |||
| * The base file to use for resolving the individual parts of | |||
| * a compound URI. | |||
| * @param uri | |||
| * The absolute URI of the file to find. | |||
| */ | |||
| FileObject findFile( String uri ) throws FileSystemException; | |||
| FileObject findFile( FileObject baseFile, String uri ) throws FileSystemException; | |||
| /** | |||
| * Creates a layered file system. | |||
| */ | |||
| FileObject createFileSystem( String scheme, FileObject file ) throws FileSystemException; | |||
| } | |||
| @@ -7,16 +7,27 @@ | |||
| */ | |||
| package org.apache.aut.vfs.provider; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.FileSystemManager; | |||
| /** | |||
| * Used for a file system provider to access the services it needs, such | |||
| * as the file system cache or other file system providers. | |||
| * | |||
| * @author Adam Murdoch | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| * @version $Revision$ $Date$ | |||
| */ | |||
| public interface FileSystemProviderContext | |||
| { | |||
| /** | |||
| * Locate a file by name. See | |||
| * {@link FileSystemManager#resolveFile(FileObject, String)} for a | |||
| * description of how this works. | |||
| */ | |||
| FileObject resolveFile( FileObject baseFile, String name ) | |||
| throws FileSystemException; | |||
| /** | |||
| * Locates a cached file system by root URI. | |||
| */ | |||
| @@ -34,13 +34,13 @@ public class ParsedUri | |||
| } | |||
| /** Returns the root URI, used to identify the file system. */ | |||
| public String getRootURI() | |||
| public String getRootUri() | |||
| { | |||
| return m_rootURI; | |||
| } | |||
| /** Sets the root URI. */ | |||
| public void setRootURI( String rootPrefix ) | |||
| public void setRootUri( String rootPrefix ) | |||
| { | |||
| m_rootURI = rootPrefix; | |||
| } | |||
| @@ -17,6 +17,7 @@ write-read-only.error=Could not write to "{0}" because it is read-only. | |||
| write-folder.error=Could not write to "{0}" because it is a folder. | |||
| write-in-use.error=Could not write to "{0}" because it is already in use. | |||
| write.error=Could not write to "{0}". | |||
| copy-file.error=Could not copy "{0}" to "{1}". | |||
| # DefaultFileContent | |||
| get-size-no-exist.error=Could not determine the size of file "{0}" because it does not exist. | |||
| @@ -30,6 +31,7 @@ close-outstr.error=Could not close file output stream. | |||
| # AbstractFileSystemProvider | |||
| invalid-absolute-uri.error=Invalid absolute URI "{0}". | |||
| not-layered-fs.error=File system is not a layered file system. | |||
| # UriParser | |||
| missing-double-slashes.error=Expecting // to follow the scheme in URI "{0}". | |||
| @@ -38,3 +40,5 @@ missing-port.error=Port number is missing from URI "{0}". | |||
| missing-hostname-path-sep.error=Expecting / to follow the hostname in URI "{0}". | |||
| invalid-childname.error=Invalid file base-name "{0}". | |||
| invalid-descendent-name.error=Invalid descendent file name "{0}". | |||
| invalid-escape-sequence.error=Invalid URI escape sequence "{0}". | |||
| invalid-relative-path.error=Invalid relative file name. | |||
| @@ -84,14 +84,41 @@ public class UriParser | |||
| * Parses an absolute URI, splitting it into its components. This | |||
| * implementation assumes a "generic URI", as defined by RFC 2396. See | |||
| * {@link #parseGenericUri} for more info. | |||
| * | |||
| * <p>Sub-classes should override this method. | |||
| */ | |||
| public ParsedUri parseUri( final String uriStr ) throws FileSystemException | |||
| { | |||
| final ParsedUri retval = new ParsedUri(); | |||
| parseGenericUri( uriStr, retval ); | |||
| return retval; | |||
| // Parse the URI | |||
| final ParsedUri uri = new ParsedUri(); | |||
| parseGenericUri( uriStr, uri ); | |||
| // Build the root URI | |||
| final StringBuffer rootUri = new StringBuffer(); | |||
| appendRootUri( uri, rootUri ); | |||
| uri.setRootUri( rootUri.toString() ); | |||
| return uri; | |||
| } | |||
| /** | |||
| * Assembles a generic URI, appending to the supplied StringBuffer. | |||
| */ | |||
| protected void appendRootUri( final ParsedUri uri, final StringBuffer rootUri ) | |||
| { | |||
| rootUri.append( uri.getScheme() ); | |||
| rootUri.append( "://" ); | |||
| final String userInfo = uri.getUserInfo(); | |||
| if( userInfo != null && userInfo.length() != 0 ) | |||
| { | |||
| rootUri.append( userInfo ); | |||
| rootUri.append( "@" ); | |||
| } | |||
| rootUri.append( uri.getHostName() ); | |||
| final String port = uri.getPort(); | |||
| if( port != null && port.length() > 0 ) | |||
| { | |||
| rootUri.append( ":" ); | |||
| rootUri.append( port ); | |||
| } | |||
| } | |||
| /** | |||
| @@ -119,7 +146,8 @@ public class UriParser | |||
| // Extract the scheme and authority parts | |||
| extractToPath( uriStr, name, uri ); | |||
| // Normalise the file name | |||
| // Decode and normalise the file name | |||
| decode( name, 0, name.length() ); | |||
| normalisePath( name ); | |||
| uri.setPath( name.toString() ); | |||
| @@ -128,7 +156,7 @@ public class UriParser | |||
| rootUri.append( uri.getScheme() ); | |||
| rootUri.append( "://" ); | |||
| rootUri.append( uri.getHostName() ); | |||
| uri.setRootURI( rootUri.toString() ); | |||
| uri.setRootUri( rootUri.toString() ); | |||
| } | |||
| /** | |||
| @@ -404,9 +432,9 @@ public class UriParser | |||
| if( scope == NameScope.CHILD ) | |||
| { | |||
| final int baseLen = baseFile.length(); | |||
| if( ! resolvedPath.startsWith( baseFile ) | |||
| if( !resolvedPath.startsWith( baseFile ) | |||
| || resolvedPath.length() == baseLen | |||
| || resolvedPath.charAt( baseLen ) != m_separatorChar | |||
| || ( baseLen > 1 && resolvedPath.charAt( baseLen ) != m_separatorChar ) | |||
| || resolvedPath.indexOf( m_separatorChar, baseLen + 1 ) != -1 ) | |||
| { | |||
| final String message = REZ.getString( "invalid-childname.error", path ); | |||
| @@ -416,9 +444,9 @@ public class UriParser | |||
| else if( scope == NameScope.DESCENDENT ) | |||
| { | |||
| final int baseLen = baseFile.length(); | |||
| if( ! resolvedPath.startsWith( baseFile ) | |||
| if( !resolvedPath.startsWith( baseFile ) | |||
| || resolvedPath.length() == baseLen | |||
| || resolvedPath.charAt( baseLen ) != m_separatorChar ) | |||
| || ( baseLen > 1 && resolvedPath.charAt( baseLen ) != m_separatorChar ) ) | |||
| { | |||
| final String message = REZ.getString( "invalid-descendent-name.error", path ); | |||
| throw new FileSystemException( message ); | |||
| @@ -463,7 +491,8 @@ public class UriParser | |||
| * <li>Removes trailing separator. | |||
| * </ul> | |||
| */ | |||
| public void normalisePath( final StringBuffer path ) throws FileSystemException | |||
| public void normalisePath( final StringBuffer path ) | |||
| throws FileSystemException | |||
| { | |||
| if( path.length() == 0 ) | |||
| { | |||
| @@ -515,14 +544,20 @@ public class UriParser | |||
| path.charAt( startElem + 1 ) == '.' ) | |||
| { | |||
| // A '..' element - remove the previous element | |||
| if( startElem > startFirstElem ) | |||
| if( startElem == startFirstElem ) | |||
| { | |||
| int pos = startElem - 2; | |||
| for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos -- ) | |||
| { | |||
| } | |||
| startElem = pos + 1; | |||
| // Previous element is missing | |||
| final String message = REZ.getString( "invalid-relative-path.error" ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| // Find start of previous element | |||
| int pos = startElem - 2; | |||
| for( ; pos >= 0 && path.charAt( pos ) != m_separatorChar; pos-- ) | |||
| { | |||
| } | |||
| startElem = pos + 1; | |||
| path.delete( startElem, endElem + 1 ); | |||
| maxlen = path.length(); | |||
| continue; | |||
| @@ -595,7 +630,8 @@ public class UriParser | |||
| * @return | |||
| * The scheme name. Returns null if there is no scheme. | |||
| */ | |||
| protected static String extractScheme( final String uri, final StringBuffer buffer ) | |||
| public static String extractScheme( final String uri, | |||
| final StringBuffer buffer ) | |||
| { | |||
| if( buffer != null ) | |||
| { | |||
| @@ -642,4 +678,103 @@ public class UriParser | |||
| // No scheme in URI | |||
| return null; | |||
| } | |||
| /** | |||
| * Removes %nn encodings from a string. | |||
| */ | |||
| public static String decode( final String encodedStr ) | |||
| throws FileSystemException | |||
| { | |||
| final StringBuffer buffer = new StringBuffer( encodedStr ); | |||
| decode( buffer, 0, buffer.length() ); | |||
| return buffer.toString(); | |||
| } | |||
| /** | |||
| * Removes %nn encodings from a string. | |||
| */ | |||
| public static void decode( final StringBuffer buffer, | |||
| final int offset, | |||
| final int length ) | |||
| throws FileSystemException | |||
| { | |||
| int index = offset; | |||
| int count = length; | |||
| for( ; count > 0; count--, index++ ) | |||
| { | |||
| final char ch = buffer.charAt( index ); | |||
| if( ch != '%' ) | |||
| { | |||
| continue; | |||
| } | |||
| if( count < 3 ) | |||
| { | |||
| final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + count ) ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| // Decode | |||
| int dig1 = Character.digit( buffer.charAt( index + 1 ), 16 ); | |||
| int dig2 = Character.digit( buffer.charAt( index + 2 ), 16 ); | |||
| if( dig1 == -1 || dig2 == -1 ) | |||
| { | |||
| final String message = REZ.getString( "invalid-escape-sequence.error", buffer.substring( index, index + 3 ) ); | |||
| throw new FileSystemException( message ); | |||
| } | |||
| char value = (char)( dig1 << 4 | dig2 ); | |||
| // Replace | |||
| buffer.setCharAt( index, value ); | |||
| buffer.delete( index + 1, index + 3 ); | |||
| count -= 2; | |||
| } | |||
| } | |||
| /** | |||
| * Encodes and appends a string to a StringBuffer. | |||
| */ | |||
| public static void appendEncoded( final StringBuffer buffer, | |||
| final String unencodedValue, | |||
| final char[] reserved ) | |||
| { | |||
| final int offset = buffer.length(); | |||
| buffer.append( unencodedValue ); | |||
| encode( buffer, offset, unencodedValue.length(), reserved ); | |||
| } | |||
| /** | |||
| * Encodes a set of reserved characters in a StringBuffer, using the URI | |||
| * %nn encoding. Always encodes % characters. | |||
| */ | |||
| public static void encode( final StringBuffer buffer, | |||
| final int offset, | |||
| final int length, | |||
| final char[] reserved ) | |||
| { | |||
| int index = offset; | |||
| int count = length; | |||
| for( ; count > 0; index++, count-- ) | |||
| { | |||
| final char ch = buffer.charAt( index ); | |||
| boolean match = ( ch == '%' ); | |||
| for( int i = 0; !match && i < reserved.length; i++ ) | |||
| { | |||
| if( ch == reserved[ i ] ) | |||
| { | |||
| match = true; | |||
| } | |||
| } | |||
| if( match ) | |||
| { | |||
| // Encode | |||
| char[] digits = { | |||
| Character.forDigit( ( ( ch >> 4 ) & 0xF ), 16 ), | |||
| Character.forDigit( ( ch & 0xF ), 16 ) | |||
| }; | |||
| buffer.setCharAt( index, '%' ); | |||
| buffer.insert( index + 1, digits ); | |||
| index += 2; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -8,7 +8,6 @@ | |||
| package org.apache.aut.vfs.provider.ftp; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.ParsedUri; | |||
| import org.apache.aut.vfs.provider.UriParser; | |||
| /** | |||
| @@ -21,15 +20,27 @@ public class FtpFileNameParser extends UriParser | |||
| /** | |||
| * Parses an absolute URI, splitting it into its components. | |||
| */ | |||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||
| public ParsedFtpUri parseFtpUri( final String uriStr ) | |||
| throws FileSystemException | |||
| { | |||
| ParsedFtpUri uri = new ParsedFtpUri(); | |||
| final ParsedFtpUri uri = new ParsedFtpUri(); | |||
| // FTP URI are generic URI (as per RFC 2396) | |||
| parseGenericUri( uriStr, uri ); | |||
| // Adjust the hostname to lower-case | |||
| final String hostname = uri.getHostName().toLowerCase(); | |||
| uri.setHostName( hostname ); | |||
| // Drop the port if it is 21 | |||
| final String port = uri.getPort(); | |||
| if( port != null && port.equals( "21" ) ) | |||
| { | |||
| uri.setPort( null ); | |||
| } | |||
| // Split up the userinfo into a username and password | |||
| String userInfo = uri.getUserInfo(); | |||
| final String userInfo = uri.getUserInfo(); | |||
| if( userInfo != null ) | |||
| { | |||
| int idx = userInfo.indexOf( ':' ); | |||
| @@ -46,6 +57,11 @@ public class FtpFileNameParser extends UriParser | |||
| } | |||
| } | |||
| // Now build the root URI | |||
| final StringBuffer rootUri = new StringBuffer(); | |||
| appendRootUri( uri, rootUri ); | |||
| uri.setRootUri( rootUri.toString() ); | |||
| return uri; | |||
| } | |||
| } | |||
| @@ -8,39 +8,44 @@ | |||
| package org.apache.aut.vfs.provider.ftp; | |||
| import org.apache.aut.vfs.FileName; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||
| import org.apache.aut.vfs.provider.DefaultFileName; | |||
| import org.apache.aut.vfs.provider.FileSystem; | |||
| import org.apache.aut.vfs.provider.ParsedUri; | |||
| import org.apache.aut.vfs.provider.UriParser; | |||
| /** | |||
| * A provider for FTP file systems. | |||
| * | |||
| * @author Adam Murdoch | |||
| * | |||
| * @ant:type type="file-system" name="ftp" | |||
| */ | |||
| public class FtpFileSystemProvider extends AbstractFileSystemProvider | |||
| { | |||
| private UriParser m_parser = new FtpFileNameParser(); | |||
| private final FtpFileNameParser m_parser = new FtpFileNameParser(); | |||
| /** | |||
| * Parses a URI into its components. | |||
| */ | |||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||
| protected ParsedUri parseUri( final FileObject baseFile, | |||
| final String uri ) | |||
| throws FileSystemException | |||
| { | |||
| return m_parser.parseUri( uri ); | |||
| return m_parser.parseFtpUri( uri ); | |||
| } | |||
| /** | |||
| * Creates the filesystem. | |||
| */ | |||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||
| throws FileSystemException | |||
| { | |||
| ParsedFtpUri ftpUri = (ParsedFtpUri)uri; | |||
| final ParsedFtpUri ftpUri = (ParsedFtpUri)uri; | |||
| // Build the root name | |||
| FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootURI(), "/" ); | |||
| final FileName rootName = new DefaultFileName( m_parser, ftpUri.getRootUri(), "/" ); | |||
| // Determine the username and password to use | |||
| String username = ftpUri.getUserName(); | |||
| @@ -50,21 +50,22 @@ abstract class LocalFileNameParser | |||
| * | |||
| * @param uriStr The URI. | |||
| */ | |||
| public ParsedUri parseUri( final String uriStr ) | |||
| public ParsedUri parseFileUri( final String uriStr ) | |||
| throws FileSystemException | |||
| { | |||
| StringBuffer name = new StringBuffer(); | |||
| ParsedFileUri uri = new ParsedFileUri(); | |||
| final StringBuffer name = new StringBuffer(); | |||
| final ParsedFileUri uri = new ParsedFileUri(); | |||
| // Extract the scheme | |||
| String scheme = extractScheme( uriStr, name ); | |||
| final String scheme = extractScheme( uriStr, name ); | |||
| uri.setScheme( scheme ); | |||
| // Adjust the separators | |||
| // Remove encoding, and adjust the separators | |||
| decode( name, 0, name.length() ); | |||
| fixSeparators( name ); | |||
| // Extract the root prefix | |||
| String rootFile = extractRootPrefix( uriStr, name ); | |||
| final String rootFile = extractRootPrefix( uriStr, name ); | |||
| uri.setRootFile( rootFile ); | |||
| // Normalise the path | |||
| @@ -72,11 +73,11 @@ abstract class LocalFileNameParser | |||
| uri.setPath( name.toString() ); | |||
| // Build the root URI | |||
| StringBuffer rootUri = new StringBuffer(); | |||
| final StringBuffer rootUri = new StringBuffer(); | |||
| rootUri.append( scheme ); | |||
| rootUri.append( "://" ); | |||
| rootUri.append( rootFile ); | |||
| uri.setRootURI( rootUri.toString() ); | |||
| uri.setRootUri( rootUri.toString() ); | |||
| return uri; | |||
| } | |||
| @@ -51,20 +51,22 @@ public class LocalFileSystemProvider extends AbstractFileSystemProvider | |||
| /** | |||
| * Finds a local file, from its local name. | |||
| */ | |||
| public FileObject findLocalFile( final String name ) throws FileSystemException | |||
| public FileObject findLocalFile( final String name ) | |||
| throws FileSystemException | |||
| { | |||
| // TODO - tidy this up, no need to turn the name into an absolute URI, | |||
| // and then straight back again | |||
| return findFile( "file:" + name ); | |||
| return findFile( null, "file:" + name ); | |||
| } | |||
| /** | |||
| * Finds a local file. | |||
| */ | |||
| public FileObject findFileByLocalName( final File file ) throws FileSystemException | |||
| public FileObject findLocalFile( final File file ) | |||
| throws FileSystemException | |||
| { | |||
| // TODO - tidy this up, should build file object straight from the file | |||
| return findFile( "file:" + file.getAbsolutePath() ); | |||
| return findFile( null, "file:" + file.getAbsolutePath() ); | |||
| } | |||
| /** | |||
| @@ -75,22 +77,25 @@ public class LocalFileSystemProvider extends AbstractFileSystemProvider | |||
| * <p>The provider can annotate this object with any additional | |||
| * information it requires to create a file system from the URI. | |||
| */ | |||
| protected ParsedUri parseURI( final String uri ) throws FileSystemException | |||
| protected ParsedUri parseUri( final FileObject baseFile, | |||
| final String uri ) | |||
| throws FileSystemException | |||
| { | |||
| return m_parser.parseUri( uri ); | |||
| return m_parser.parseFileUri( uri ); | |||
| } | |||
| /** | |||
| * Creates the filesystem. | |||
| */ | |||
| protected FileSystem createFileSystem( final ParsedUri uri ) throws FileSystemException | |||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||
| throws FileSystemException | |||
| { | |||
| // Build the name of the root file. | |||
| final ParsedFileUri fileUri = (ParsedFileUri)uri; | |||
| final String rootFile = fileUri.getRootFile(); | |||
| // Create the file system | |||
| final DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootURI(), "/" ); | |||
| final DefaultFileName rootName = new DefaultFileName( m_parser, fileUri.getRootUri(), "/" ); | |||
| return new LocalFileSystem( rootName, rootFile ); | |||
| } | |||
| } | |||
| @@ -27,19 +27,27 @@ public class SmbFileNameParser | |||
| /** | |||
| * Parses an absolute URI, splitting it into its components. | |||
| */ | |||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||
| public ParsedUri parseSmbUri( final String uriStr ) | |||
| throws FileSystemException | |||
| { | |||
| ParsedSmbUri uri = new ParsedSmbUri(); | |||
| StringBuffer name = new StringBuffer(); | |||
| final ParsedSmbUri uri = new ParsedSmbUri(); | |||
| final StringBuffer name = new StringBuffer(); | |||
| // Extract the scheme and authority parts | |||
| extractToPath( uriStr, name, uri ); | |||
| // Normalise paths | |||
| // Convert the hostname to lowercase | |||
| final String hostname = uri.getHostName().toLowerCase(); | |||
| uri.setHostName( hostname ); | |||
| // TODO - drop the default port | |||
| // Decode and adjust separators | |||
| decode( name, 0, name.length() ); | |||
| fixSeparators( name ); | |||
| // Extract the share | |||
| String share = extractFirstElement( name ); | |||
| final String share = extractFirstElement( name ); | |||
| if( share == null ) | |||
| { | |||
| final String message = REZ.getString( "missing-share-name.error", uriStr ); | |||
| @@ -47,23 +55,18 @@ public class SmbFileNameParser | |||
| } | |||
| uri.setShare( share ); | |||
| // Normalise the path | |||
| normalisePath( name ); | |||
| // Set the path | |||
| uri.setPath( name.toString() ); | |||
| // Set the root URI | |||
| StringBuffer rootUri = new StringBuffer(); | |||
| rootUri.append( uri.getScheme() ); | |||
| rootUri.append( "://" ); | |||
| String userInfo = uri.getUserInfo(); | |||
| if( userInfo != null ) | |||
| { | |||
| rootUri.append( userInfo ); | |||
| rootUri.append( '@' ); | |||
| } | |||
| rootUri.append( uri.getHostName() ); | |||
| appendRootUri( uri, rootUri ); | |||
| rootUri.append( '/' ); | |||
| rootUri.append( share ); | |||
| uri.setRootURI( rootUri.toString() ); | |||
| uri.setRootUri( rootUri.toString() ); | |||
| return uri; | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| package org.apache.aut.vfs.provider.smb; | |||
| import org.apache.aut.vfs.FileName; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||
| import org.apache.aut.vfs.provider.DefaultFileName; | |||
| @@ -19,27 +20,31 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||
| * A provider for SMB (Samba, Windows share) file systems. | |||
| * | |||
| * @author Adam Murdoch | |||
| * | |||
| * @ant:type type="file-system" name="smb" | |||
| */ | |||
| public class SmbFileSystemProvider extends AbstractFileSystemProvider implements FileSystemProvider | |||
| { | |||
| SmbFileNameParser m_parser = new SmbFileNameParser(); | |||
| private final SmbFileNameParser m_parser = new SmbFileNameParser(); | |||
| /** | |||
| * Parses a URI into its components. | |||
| */ | |||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||
| protected ParsedUri parseUri( final FileObject baseFile, | |||
| final String uri ) | |||
| throws FileSystemException | |||
| { | |||
| return m_parser.parseUri( uri ); | |||
| return m_parser.parseSmbUri( uri ); | |||
| } | |||
| /** | |||
| * Creates the filesystem. | |||
| */ | |||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||
| throws FileSystemException | |||
| { | |||
| ParsedSmbUri smbUri = (ParsedSmbUri)uri; | |||
| FileName rootName = new DefaultFileName( m_parser, smbUri.getRootURI(), "/" ); | |||
| final ParsedSmbUri smbUri = (ParsedSmbUri)uri; | |||
| final FileName rootName = new DefaultFileName( m_parser, smbUri.getRootUri(), "/" ); | |||
| return new SmbFileSystem( rootName ); | |||
| } | |||
| } | |||
| @@ -7,6 +7,7 @@ | |||
| */ | |||
| package org.apache.aut.vfs.provider.zip; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.provider.ParsedUri; | |||
| /** | |||
| @@ -16,14 +17,25 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||
| */ | |||
| public class ParsedZipUri extends ParsedUri | |||
| { | |||
| private String m_zipFile; | |||
| private String m_zipFileName; | |||
| private FileObject m_zipFile; | |||
| public String getZipFile() | |||
| public String getZipFileName() | |||
| { | |||
| return m_zipFileName; | |||
| } | |||
| public void setZipFileName( final String zipFileName ) | |||
| { | |||
| m_zipFileName = zipFileName; | |||
| } | |||
| public FileObject getZipFile() | |||
| { | |||
| return m_zipFile; | |||
| } | |||
| public void setZipFile( String zipFile ) | |||
| public void setZipFile( final FileObject zipFile ) | |||
| { | |||
| m_zipFile = zipFile; | |||
| } | |||
| @@ -8,7 +8,6 @@ | |||
| package org.apache.aut.vfs.provider.zip; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.ParsedUri; | |||
| import org.apache.aut.vfs.provider.UriParser; | |||
| /** | |||
| @@ -16,66 +15,77 @@ import org.apache.aut.vfs.provider.UriParser; | |||
| * | |||
| * @author Adam Murdoch | |||
| */ | |||
| public class ZipFileNameParser extends UriParser | |||
| public class ZipFileNameParser | |||
| extends UriParser | |||
| { | |||
| private static final char[] ZIP_URL_RESERVED_CHARS = { '!' }; | |||
| /** | |||
| * Parses an absolute URI, splitting it into its components. | |||
| * | |||
| * @param name | |||
| * @param uriStr | |||
| * The URI. | |||
| */ | |||
| public ParsedUri parseUri( String uriStr ) throws FileSystemException | |||
| public ParsedZipUri parseZipUri( final String uriStr ) | |||
| throws FileSystemException | |||
| { | |||
| StringBuffer name = new StringBuffer(); | |||
| ParsedZipUri uri = new ParsedZipUri(); | |||
| final StringBuffer name = new StringBuffer(); | |||
| final ParsedZipUri uri = new ParsedZipUri(); | |||
| // Extract the scheme | |||
| String scheme = extractScheme( uriStr, name ); | |||
| final String scheme = extractScheme( uriStr, name ); | |||
| uri.setScheme( scheme ); | |||
| // Extract the Zip file name | |||
| String zipName = extractZipName( name ); | |||
| uri.setZipFile( zipName ); | |||
| // Adjust the separators | |||
| fixSeparators( name ); | |||
| final String zipName = extractZipName( name ); | |||
| uri.setZipFileName( zipName ); | |||
| // Normalise the file name | |||
| // Decode and normalise the file name | |||
| decode( name, 0, name.length() ); | |||
| normalisePath( name ); | |||
| uri.setPath( name.toString() ); | |||
| // Build root URI | |||
| StringBuffer rootUri = new StringBuffer(); | |||
| rootUri.append( scheme ); | |||
| return uri; | |||
| } | |||
| /** | |||
| * Assembles a root URI from the components of a parsed URI. | |||
| */ | |||
| public String buildRootUri( final ParsedZipUri uri ) | |||
| { | |||
| final StringBuffer rootUri = new StringBuffer(); | |||
| rootUri.append( uri.getScheme() ); | |||
| rootUri.append( ":" ); | |||
| rootUri.append( zipName ); | |||
| appendEncoded( rootUri, uri.getZipFile().getName().getURI(), ZIP_URL_RESERVED_CHARS ); | |||
| rootUri.append( "!" ); | |||
| uri.setRootURI( rootUri.toString() ); | |||
| return uri; | |||
| return rootUri.toString(); | |||
| } | |||
| /** | |||
| * Pops the root prefix off a URI, which has had the scheme removed. | |||
| */ | |||
| protected String extractZipName( StringBuffer uri ) throws FileSystemException | |||
| private String extractZipName( final StringBuffer uri ) | |||
| throws FileSystemException | |||
| { | |||
| // Looking for <name>!<abspath> | |||
| // TODO - how does '!' in the file name get escaped? | |||
| int maxlen = uri.length(); | |||
| for( int pos = 0; pos < maxlen; pos++ ) | |||
| int pos = 0; | |||
| for( ; pos < maxlen && uri.charAt( pos ) != '!'; pos++ ) | |||
| { | |||
| } | |||
| // Extract the name | |||
| String prefix = uri.substring( 0, pos ); | |||
| if( pos < maxlen ) | |||
| { | |||
| uri.delete( 0, pos + 1 ); | |||
| } | |||
| else | |||
| { | |||
| if( uri.charAt( pos ) == '!' ) | |||
| { | |||
| String prefix = uri.substring( 0, pos ); | |||
| uri.delete( 0, pos + 1 ); | |||
| return prefix; | |||
| } | |||
| uri.setLength( 0 ); | |||
| } | |||
| // Assume the URI is the Jar file name | |||
| String prefix = uri.toString(); | |||
| uri.setLength( 0 ); | |||
| return prefix; | |||
| // Decode the name | |||
| return decode( prefix ); | |||
| } | |||
| } | |||
| @@ -8,6 +8,8 @@ | |||
| package org.apache.aut.vfs.provider.zip; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import org.apache.aut.vfs.FileObject; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.aut.vfs.provider.AbstractFileSystemProvider; | |||
| import org.apache.aut.vfs.provider.DefaultFileName; | |||
| @@ -20,31 +22,83 @@ import org.apache.aut.vfs.provider.ParsedUri; | |||
| * systems, for local Zip files only. | |||
| * | |||
| * @author Adam Murdoch | |||
| * | |||
| * @ant:type type="file-system" name="zip" | |||
| */ | |||
| public class ZipFileSystemProvider extends AbstractFileSystemProvider | |||
| public class ZipFileSystemProvider | |||
| extends AbstractFileSystemProvider | |||
| implements FileSystemProvider | |||
| { | |||
| private ZipFileNameParser m_parser = new ZipFileNameParser(); | |||
| private final ZipFileNameParser m_parser = new ZipFileNameParser(); | |||
| /** | |||
| * Parses a URI into its components. | |||
| */ | |||
| protected ParsedUri parseURI( String uri ) throws FileSystemException | |||
| protected ParsedUri parseUri( final FileObject baseFile, | |||
| final String uriStr ) | |||
| throws FileSystemException | |||
| { | |||
| return m_parser.parseUri( uri ); | |||
| // Parse the URI | |||
| final ParsedZipUri uri = m_parser.parseZipUri( uriStr ); | |||
| // Make the URI canonical | |||
| // Resolve the Zip file name | |||
| final String fileName = uri.getZipFileName(); | |||
| final FileObject file = getContext().resolveFile( baseFile, fileName ); | |||
| uri.setZipFile( file ); | |||
| // Rebuild the root URI | |||
| final String rootUri = m_parser.buildRootUri( uri ); | |||
| uri.setRootUri( rootUri ); | |||
| return uri; | |||
| } | |||
| /** | |||
| * Builds the URI for the root of a layered file system. | |||
| */ | |||
| protected ParsedUri buildUri( final String scheme, | |||
| final FileObject file ) | |||
| throws FileSystemException | |||
| { | |||
| ParsedZipUri uri = new ParsedZipUri(); | |||
| uri.setScheme( scheme ); | |||
| uri.setZipFile( file ); | |||
| final String rootUri = m_parser.buildRootUri( uri ); | |||
| uri.setRootUri( rootUri ); | |||
| uri.setPath( "/" ); | |||
| return uri; | |||
| } | |||
| /** | |||
| * Creates the filesystem. | |||
| */ | |||
| protected FileSystem createFileSystem( ParsedUri uri ) throws FileSystemException | |||
| protected FileSystem createFileSystem( final ParsedUri uri ) | |||
| throws FileSystemException | |||
| { | |||
| // Locate the Zip file | |||
| ParsedZipUri zipUri = (ParsedZipUri)uri; | |||
| String fileName = zipUri.getZipFile(); | |||
| // TODO - use the context to resolve zip file to a FileObject | |||
| File file = new File( fileName ).getAbsoluteFile(); | |||
| DefaultFileName name = new DefaultFileName( m_parser, zipUri.getRootURI(), "/" ); | |||
| return new ZipFileSystem( name, file ); | |||
| final ParsedZipUri zipUri = (ParsedZipUri)uri; | |||
| final FileObject file = zipUri.getZipFile(); | |||
| // TODO - temporary hack; need to use a converter | |||
| File destFile = null; | |||
| try | |||
| { | |||
| final File cacheDir = new File( "ant_vfs_cache" ); | |||
| cacheDir.mkdirs(); | |||
| destFile = File.createTempFile( "cache_", "_" + file.getName().getBaseName(), cacheDir ); | |||
| destFile.deleteOnExit(); | |||
| } | |||
| catch( IOException e ) | |||
| { | |||
| throw new FileSystemException( "Could not replicate file", e ); | |||
| } | |||
| FileObject destFileObj = getContext().resolveFile( null, destFile.getAbsolutePath() ); | |||
| destFileObj.copy( file ); | |||
| // Create the file system | |||
| DefaultFileName name = new DefaultFileName( m_parser, zipUri.getRootUri(), "/" ); | |||
| return new ZipFileSystem( name, destFile ); | |||
| } | |||
| } | |||
| @@ -1,2 +1,3 @@ | |||
| missing-home-dir.error=Cannot locate antRun scripts: Property 'myrmidon.home' not specified | |||
| create-vfs-manager.error=Could not create the VFS manager. | |||
| create-vfs-manager.error=Could not create the VFS manager. | |||
| create-provider.error=Could not create file system provider "{0}". | |||
| @@ -0,0 +1,108 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.myrmidon.framework.factories; | |||
| import org.apache.aut.vfs.impl.DefaultFileSystemManager; | |||
| import org.apache.aut.vfs.provider.FileSystemProvider; | |||
| import org.apache.aut.vfs.FileSystemException; | |||
| import org.apache.avalon.framework.activity.Disposable; | |||
| import org.apache.avalon.framework.activity.Initializable; | |||
| import org.apache.avalon.framework.service.Serviceable; | |||
| import org.apache.avalon.framework.service.ServiceManager; | |||
| import org.apache.avalon.framework.service.ServiceException; | |||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | |||
| import org.apache.avalon.excalibur.i18n.Resources; | |||
| import org.apache.myrmidon.interfaces.type.TypeManager; | |||
| import org.apache.myrmidon.interfaces.type.TypeFactory; | |||
| import org.apache.myrmidon.interfaces.type.TypeException; | |||
| /** | |||
| * The myrmidon FileSystemManager implementation. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| * @version $Revision$ $Date$ | |||
| */ | |||
| public class VfsManager | |||
| extends DefaultFileSystemManager | |||
| implements Serviceable, Initializable, Disposable | |||
| { | |||
| private final static Resources REZ | |||
| = ResourceManager.getPackageResources( VfsManager.class ); | |||
| private TypeFactory m_typeFactory; | |||
| /** | |||
| * Locate the services used by this service. | |||
| */ | |||
| public void service( final ServiceManager serviceManager ) throws ServiceException | |||
| { | |||
| final TypeManager typeManager = (TypeManager)serviceManager.lookup( TypeManager.ROLE ); | |||
| try | |||
| { | |||
| m_typeFactory = typeManager.getFactory( FileSystemProvider.class ); | |||
| } | |||
| catch( TypeException e ) | |||
| { | |||
| throw new ServiceException( e.getMessage(), e ); | |||
| } | |||
| } | |||
| /** | |||
| * Initialises this service. | |||
| */ | |||
| public void initialize() throws Exception | |||
| { | |||
| // TODO - make this list configurable | |||
| // Required providers | |||
| addProvider( new String[] { "zip", "jar" }, "zip", false ); | |||
| // Optional providers | |||
| addProvider( new String[] { "smb" }, "smb", true ); | |||
| addProvider( new String[] { "ftp" }, "ftp", true ); | |||
| } | |||
| /** | |||
| * Disposes this service. | |||
| */ | |||
| public void dispose() | |||
| { | |||
| // Clean-up | |||
| close(); | |||
| } | |||
| /** | |||
| * Registers a file system provider. | |||
| */ | |||
| public void addProvider( final String[] urlSchemes, | |||
| final String providerName, | |||
| final boolean ignoreIfNotPresent ) | |||
| throws FileSystemException | |||
| { | |||
| // Create an instance | |||
| if( ignoreIfNotPresent && ! m_typeFactory.canCreate( providerName ) ) | |||
| { | |||
| return; | |||
| } | |||
| final FileSystemProvider provider; | |||
| try | |||
| { | |||
| provider = (FileSystemProvider)m_typeFactory.create( providerName ); | |||
| } | |||
| catch( Exception e ) | |||
| { | |||
| final String message = REZ.getString( "create-provider.error", providerName ); | |||
| throw new FileSystemException( message, e ); | |||
| } | |||
| // Register the provider | |||
| addProvider( urlSchemes, provider ); | |||
| } | |||
| } | |||
| @@ -8,7 +8,6 @@ | |||
| package org.apache.myrmidon.framework.factories; | |||
| import org.apache.aut.vfs.FileSystemManager; | |||
| import org.apache.aut.vfs.impl.DefaultFileSystemManager; | |||
| import org.apache.avalon.excalibur.i18n.ResourceManager; | |||
| import org.apache.avalon.excalibur.i18n.Resources; | |||
| import org.apache.myrmidon.interfaces.service.AntServiceException; | |||
| @@ -34,7 +33,7 @@ public class VfsManagerFactory | |||
| { | |||
| try | |||
| { | |||
| return new DefaultFileSystemManager(); | |||
| return new VfsManager(); | |||
| } | |||
| catch( Exception e ) | |||
| { | |||
| @@ -1,9 +1,4 @@ | |||
| <services version="1.0"> | |||
| <exec-manager factory="org.apache.myrmidon.framework.factories.ExecManagerFactory"/> | |||
| <file-system-manager factory="org.apache.myrmidon.framework.factories.VfsManagerFactory"> | |||
| <provider scheme="zip" classname="org.apache.aut.vfs.provider.zip.ZipFileSystemProvider"/> | |||
| <provider scheme="jar" classname="org.apache.aut.vfs.provider.zip.ZipFileSystemProvider"/> | |||
| <provider scheme="smb" classname="org.apache.aut.vfs.provider.smb.SmbFileSystemProvider"/> | |||
| <provider scheme="ftp" classname="org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider"/> | |||
| </file-system-manager> | |||
| <file-system-manager factory="org.apache.myrmidon.framework.factories.VfsManagerFactory"/> | |||
| </services> | |||
| @@ -66,9 +66,9 @@ public abstract class AbstractFileSystemTest | |||
| } | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| * Returns the base folder to run the tests against. | |||
| */ | |||
| protected abstract String getBaseFolderURI() throws Exception; | |||
| protected abstract FileObject getBaseFolder() throws Exception; | |||
| /** | |||
| * Sets up the test | |||
| @@ -79,7 +79,10 @@ public abstract class AbstractFileSystemTest | |||
| m_manager = new DefaultFileSystemManager(); | |||
| // Locate the base folder | |||
| m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||
| m_baseFolder = getBaseFolder(); | |||
| // Make some assumptions absout the name | |||
| assertTrue( ! m_baseFolder.getName().getPath().equals( "/" ) ); | |||
| // Build the expected content of "file1.txt" | |||
| final String eol = System.getProperty( "line.separator" ); | |||
| @@ -123,6 +126,66 @@ public abstract class AbstractFileSystemTest | |||
| assertSame( "file object", m_baseFolder.getParent(), file ); | |||
| } | |||
| /** | |||
| * Tests encoding of relative URI. | |||
| */ | |||
| public void testRelativeUriEncoding() throws Exception | |||
| { | |||
| // Build base dir | |||
| m_manager.setBaseFile( m_baseFolder ); | |||
| final String path = m_baseFolder.getName().getPath(); | |||
| // Encode "some file" | |||
| FileObject file = m_manager.resolveFile( "%73%6f%6d%65%20%66%69%6c%65" ); | |||
| assertEquals( path + "/some file", file.getName().getPath() ); | |||
| // Encode "." | |||
| file = m_manager.resolveFile( "%2e" ); | |||
| assertEquals( path, file.getName().getPath() ); | |||
| // Encode '%' | |||
| file = m_manager.resolveFile( "a%25" ); | |||
| assertEquals( path + "/a%", file.getName().getPath() ); | |||
| // Encode / | |||
| file = m_manager.resolveFile( "dir%2fchild" ); | |||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||
| // Encode \ | |||
| file = m_manager.resolveFile( "dir%5cchild" ); | |||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||
| // Use "%" literal | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| // Not enough digits in encoded char | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%5" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| // Invalid digit in encoded char | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%q" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| } | |||
| /** | |||
| * Tests the root file name. | |||
| */ | |||
| @@ -176,7 +239,7 @@ public abstract class AbstractFileSystemTest | |||
| final NameScope scope ) | |||
| throws Exception | |||
| { | |||
| // Make some assumptions about the name explicit | |||
| // Make some assumptions about the name | |||
| assertTrue( !name.getPath().equals( "/" ) ); | |||
| assertTrue( !name.getPath().endsWith( "/a" ) ); | |||
| assertTrue( !name.getPath().endsWith( "/a/b" ) ); | |||
| @@ -329,6 +392,46 @@ public abstract class AbstractFileSystemTest | |||
| checkDescendentNames( baseName, NameScope.DESCENDENT ); | |||
| } | |||
| /** | |||
| * Tests resolution of absolute names. | |||
| */ | |||
| public void testAbsoluteNames() throws Exception | |||
| { | |||
| // Test against the base folder | |||
| FileName name = m_baseFolder.getName(); | |||
| checkAbsoluteNames( name ); | |||
| // Test against the root | |||
| name = m_baseFolder.getRoot().getName(); | |||
| checkAbsoluteNames( name ); | |||
| // Test against some unknown file | |||
| name = name.resolveName( "a/b/unknown" ); | |||
| checkAbsoluteNames( name ); | |||
| } | |||
| /** | |||
| * Tests resolution of absolute names. | |||
| */ | |||
| private void checkAbsoluteNames( final FileName name ) throws Exception | |||
| { | |||
| // Root | |||
| assertSameName( "/", name, "/" ); | |||
| assertSameName( "/", name, "//" ); | |||
| assertSameName( "/", name, "/." ); | |||
| assertSameName( "/", name, "/some file/.." ); | |||
| // Some absolute names | |||
| assertSameName( "/a", name, "/a" ); | |||
| assertSameName( "/a", name, "/./a" ); | |||
| assertSameName( "/a", name, "/a/." ); | |||
| assertSameName( "/a/b", name, "/a/b" ); | |||
| // Some bad names | |||
| assertBadName( name, "/..", NameScope.FILE_SYSTEM ); | |||
| assertBadName( name, "/a/../..", NameScope.FILE_SYSTEM ); | |||
| } | |||
| /** | |||
| * Asserts that a particular relative name is invalid for a particular | |||
| * scope. | |||
| @@ -340,7 +443,7 @@ public abstract class AbstractFileSystemTest | |||
| try | |||
| { | |||
| name.resolveName( relName, scope ); | |||
| fail(); | |||
| fail( "expected failure" ); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| @@ -16,7 +16,8 @@ import java.util.Set; | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemTest | |||
| public abstract class AbstractWritableFileSystemTest | |||
| extends AbstractFileSystemTest | |||
| { | |||
| public AbstractWritableFileSystemTest( String name ) | |||
| { | |||
| @@ -26,14 +27,14 @@ public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemT | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected abstract String getWriteFolderURI() throws Exception; | |||
| protected abstract FileObject getWriteFolder() throws Exception; | |||
| /** | |||
| * Sets up a scratch folder for the test to use. | |||
| */ | |||
| protected FileObject createScratchFolder() throws Exception | |||
| { | |||
| FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||
| FileObject scratchFolder = getWriteFolder(); | |||
| // Make sure the test folder is empty | |||
| scratchFolder.delete(); | |||
| @@ -7,12 +7,15 @@ | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider; | |||
| /** | |||
| * Tests for FTP file systems. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||
| public class FtpFileSystemTest | |||
| extends AbstractWritableFileSystemTest | |||
| { | |||
| public FtpFileSystemTest( String name ) | |||
| { | |||
| @@ -22,16 +25,19 @@ public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||
| m_manager.addProvider( "ftp", new FtpFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -24,21 +24,19 @@ public class LocalFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| throws Exception | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| final File testDir = getTestResource( "basedir" ); | |||
| return testDir.toURL().toString(); | |||
| return m_manager.convert( testDir ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| throws Exception | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| final File testDir = getTestResource( "write-tests" ); | |||
| return testDir.toURL().toString(); | |||
| return m_manager.convert( testDir ); | |||
| } | |||
| /** | |||
| @@ -0,0 +1,41 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||
| /** | |||
| * Tests for the Zip file system, using a zip file nested inside another zip file. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public class NestedZipFileSystemTest | |||
| extends AbstractReadOnlyFileSystemTest | |||
| { | |||
| public NestedZipFileSystemTest( String name ) | |||
| { | |||
| super( name ); | |||
| } | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||
| // Locate the base Zip file | |||
| final String zipFilePath = getTestResource( "nested.zip" ).getAbsolutePath(); | |||
| String uri = "zip:" + zipFilePath + "!/test.zip"; | |||
| final FileObject zipFile = m_manager.resolveFile( uri ); | |||
| // Now build the nested file system | |||
| final FileObject nestedFS = m_manager.createFileSystem( "zip", zipFile ); | |||
| return nestedFS.resolveFile( "/basedir" ); | |||
| } | |||
| } | |||
| @@ -7,6 +7,8 @@ | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.smb.SmbFileSystemProvider; | |||
| /** | |||
| * Tests for the SMB file system. | |||
| * | |||
| @@ -22,16 +24,19 @@ public class SmbFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||
| final String uri = System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||
| m_manager.addProvider( "smb", new SmbFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||
| final String uri = System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| package org.apache.aut.vfs; | |||
| import java.io.File; | |||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||
| /** | |||
| * Tests for the Zip file system. | |||
| @@ -24,10 +25,11 @@ public class ZipFileSystemTest extends AbstractReadOnlyFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| File zipFile = getTestResource( "test.zip" ); | |||
| String uri = "zip:" + zipFile + "!basedir"; | |||
| return uri; | |||
| String uri = "zip:" + zipFile.getAbsolutePath() + "!basedir"; | |||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -66,9 +66,9 @@ public abstract class AbstractFileSystemTest | |||
| } | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| * Returns the base folder to run the tests against. | |||
| */ | |||
| protected abstract String getBaseFolderURI() throws Exception; | |||
| protected abstract FileObject getBaseFolder() throws Exception; | |||
| /** | |||
| * Sets up the test | |||
| @@ -79,7 +79,10 @@ public abstract class AbstractFileSystemTest | |||
| m_manager = new DefaultFileSystemManager(); | |||
| // Locate the base folder | |||
| m_baseFolder = m_manager.resolveFile( getBaseFolderURI() ); | |||
| m_baseFolder = getBaseFolder(); | |||
| // Make some assumptions absout the name | |||
| assertTrue( ! m_baseFolder.getName().getPath().equals( "/" ) ); | |||
| // Build the expected content of "file1.txt" | |||
| final String eol = System.getProperty( "line.separator" ); | |||
| @@ -123,6 +126,66 @@ public abstract class AbstractFileSystemTest | |||
| assertSame( "file object", m_baseFolder.getParent(), file ); | |||
| } | |||
| /** | |||
| * Tests encoding of relative URI. | |||
| */ | |||
| public void testRelativeUriEncoding() throws Exception | |||
| { | |||
| // Build base dir | |||
| m_manager.setBaseFile( m_baseFolder ); | |||
| final String path = m_baseFolder.getName().getPath(); | |||
| // Encode "some file" | |||
| FileObject file = m_manager.resolveFile( "%73%6f%6d%65%20%66%69%6c%65" ); | |||
| assertEquals( path + "/some file", file.getName().getPath() ); | |||
| // Encode "." | |||
| file = m_manager.resolveFile( "%2e" ); | |||
| assertEquals( path, file.getName().getPath() ); | |||
| // Encode '%' | |||
| file = m_manager.resolveFile( "a%25" ); | |||
| assertEquals( path + "/a%", file.getName().getPath() ); | |||
| // Encode / | |||
| file = m_manager.resolveFile( "dir%2fchild" ); | |||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||
| // Encode \ | |||
| file = m_manager.resolveFile( "dir%5cchild" ); | |||
| assertEquals( path + "/dir/child", file.getName().getPath() ); | |||
| // Use "%" literal | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| // Not enough digits in encoded char | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%5" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| // Invalid digit in encoded char | |||
| try | |||
| { | |||
| m_manager.resolveFile( "%q" ); | |||
| fail(); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| } | |||
| } | |||
| /** | |||
| * Tests the root file name. | |||
| */ | |||
| @@ -176,7 +239,7 @@ public abstract class AbstractFileSystemTest | |||
| final NameScope scope ) | |||
| throws Exception | |||
| { | |||
| // Make some assumptions about the name explicit | |||
| // Make some assumptions about the name | |||
| assertTrue( !name.getPath().equals( "/" ) ); | |||
| assertTrue( !name.getPath().endsWith( "/a" ) ); | |||
| assertTrue( !name.getPath().endsWith( "/a/b" ) ); | |||
| @@ -329,6 +392,46 @@ public abstract class AbstractFileSystemTest | |||
| checkDescendentNames( baseName, NameScope.DESCENDENT ); | |||
| } | |||
| /** | |||
| * Tests resolution of absolute names. | |||
| */ | |||
| public void testAbsoluteNames() throws Exception | |||
| { | |||
| // Test against the base folder | |||
| FileName name = m_baseFolder.getName(); | |||
| checkAbsoluteNames( name ); | |||
| // Test against the root | |||
| name = m_baseFolder.getRoot().getName(); | |||
| checkAbsoluteNames( name ); | |||
| // Test against some unknown file | |||
| name = name.resolveName( "a/b/unknown" ); | |||
| checkAbsoluteNames( name ); | |||
| } | |||
| /** | |||
| * Tests resolution of absolute names. | |||
| */ | |||
| private void checkAbsoluteNames( final FileName name ) throws Exception | |||
| { | |||
| // Root | |||
| assertSameName( "/", name, "/" ); | |||
| assertSameName( "/", name, "//" ); | |||
| assertSameName( "/", name, "/." ); | |||
| assertSameName( "/", name, "/some file/.." ); | |||
| // Some absolute names | |||
| assertSameName( "/a", name, "/a" ); | |||
| assertSameName( "/a", name, "/./a" ); | |||
| assertSameName( "/a", name, "/a/." ); | |||
| assertSameName( "/a/b", name, "/a/b" ); | |||
| // Some bad names | |||
| assertBadName( name, "/..", NameScope.FILE_SYSTEM ); | |||
| assertBadName( name, "/a/../..", NameScope.FILE_SYSTEM ); | |||
| } | |||
| /** | |||
| * Asserts that a particular relative name is invalid for a particular | |||
| * scope. | |||
| @@ -340,7 +443,7 @@ public abstract class AbstractFileSystemTest | |||
| try | |||
| { | |||
| name.resolveName( relName, scope ); | |||
| fail(); | |||
| fail( "expected failure" ); | |||
| } | |||
| catch( FileSystemException e ) | |||
| { | |||
| @@ -16,7 +16,8 @@ import java.util.Set; | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemTest | |||
| public abstract class AbstractWritableFileSystemTest | |||
| extends AbstractFileSystemTest | |||
| { | |||
| public AbstractWritableFileSystemTest( String name ) | |||
| { | |||
| @@ -26,14 +27,14 @@ public abstract class AbstractWritableFileSystemTest extends AbstractFileSystemT | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected abstract String getWriteFolderURI() throws Exception; | |||
| protected abstract FileObject getWriteFolder() throws Exception; | |||
| /** | |||
| * Sets up a scratch folder for the test to use. | |||
| */ | |||
| protected FileObject createScratchFolder() throws Exception | |||
| { | |||
| FileObject scratchFolder = m_manager.resolveFile( getWriteFolderURI() ); | |||
| FileObject scratchFolder = getWriteFolder(); | |||
| // Make sure the test folder is empty | |||
| scratchFolder.delete(); | |||
| @@ -7,12 +7,15 @@ | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.ftp.FtpFileSystemProvider; | |||
| /** | |||
| * Tests for FTP file systems. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||
| public class FtpFileSystemTest | |||
| extends AbstractWritableFileSystemTest | |||
| { | |||
| public FtpFileSystemTest( String name ) | |||
| { | |||
| @@ -22,16 +25,19 @@ public class FtpFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/read-tests"; | |||
| m_manager.addProvider( "ftp", new FtpFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||
| final String uri = System.getProperty( "test.ftp.uri" ) + "/write-tests"; | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -24,21 +24,19 @@ public class LocalFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| throws Exception | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| final File testDir = getTestResource( "basedir" ); | |||
| return testDir.toURL().toString(); | |||
| return m_manager.convert( testDir ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| throws Exception | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| final File testDir = getTestResource( "write-tests" ); | |||
| return testDir.toURL().toString(); | |||
| return m_manager.convert( testDir ); | |||
| } | |||
| /** | |||
| @@ -0,0 +1,41 @@ | |||
| /* | |||
| * Copyright (C) The Apache Software Foundation. All rights reserved. | |||
| * | |||
| * This software is published under the terms of the Apache Software License | |||
| * version 1.1, a copy of which has been included with this distribution in | |||
| * the LICENSE.txt file. | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||
| /** | |||
| * Tests for the Zip file system, using a zip file nested inside another zip file. | |||
| * | |||
| * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a> | |||
| */ | |||
| public class NestedZipFileSystemTest | |||
| extends AbstractReadOnlyFileSystemTest | |||
| { | |||
| public NestedZipFileSystemTest( String name ) | |||
| { | |||
| super( name ); | |||
| } | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||
| // Locate the base Zip file | |||
| final String zipFilePath = getTestResource( "nested.zip" ).getAbsolutePath(); | |||
| String uri = "zip:" + zipFilePath + "!/test.zip"; | |||
| final FileObject zipFile = m_manager.resolveFile( uri ); | |||
| // Now build the nested file system | |||
| final FileObject nestedFS = m_manager.createFileSystem( "zip", zipFile ); | |||
| return nestedFS.resolveFile( "/basedir" ); | |||
| } | |||
| } | |||
| @@ -7,6 +7,8 @@ | |||
| */ | |||
| package org.apache.aut.vfs; | |||
| import org.apache.aut.vfs.provider.smb.SmbFileSystemProvider; | |||
| /** | |||
| * Tests for the SMB file system. | |||
| * | |||
| @@ -22,16 +24,19 @@ public class SmbFileSystemTest extends AbstractWritableFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||
| final String uri = System.getProperty( "test.smb.uri" ) + "/read-tests"; | |||
| m_manager.addProvider( "smb", new SmbFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| /** | |||
| * Returns the URI for the area to do tests in. | |||
| */ | |||
| protected String getWriteFolderURI() | |||
| protected FileObject getWriteFolder() throws Exception | |||
| { | |||
| return System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||
| final String uri = System.getProperty( "test.smb.uri" ) + "/write-tests"; | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| package org.apache.aut.vfs; | |||
| import java.io.File; | |||
| import org.apache.aut.vfs.provider.zip.ZipFileSystemProvider; | |||
| /** | |||
| * Tests for the Zip file system. | |||
| @@ -24,10 +25,11 @@ public class ZipFileSystemTest extends AbstractReadOnlyFileSystemTest | |||
| /** | |||
| * Returns the URI for the base folder. | |||
| */ | |||
| protected String getBaseFolderURI() | |||
| protected FileObject getBaseFolder() throws Exception | |||
| { | |||
| File zipFile = getTestResource( "test.zip" ); | |||
| String uri = "zip:" + zipFile + "!basedir"; | |||
| return uri; | |||
| String uri = "zip:" + zipFile.getAbsolutePath() + "!basedir"; | |||
| m_manager.addProvider( "zip", new ZipFileSystemProvider() ); | |||
| return m_manager.resolveFile( uri ); | |||
| } | |||
| } | |||
| @@ -37,15 +37,22 @@ | |||
| <p>The VFS needs plenty of work:</p> | |||
| <ul> | |||
| <li>Move and copy files/folders.</li> | |||
| <li>Move files/folders.</li> | |||
| <li>Recursive folders copy.</li> | |||
| <li>Search through a file hierarchy, using Ant-style wildcards.</li> | |||
| <li>Search through a file hierarchy, using a Selector interface.</li> | |||
| <li>The in-memory caching mechanism is pretty rudimentary at this stage. | |||
| It needs work to make it size capped. In addition, some mechanism needs | |||
| to be provided to release and refresh cached info. | |||
| </li> | |||
| <li>Convert files/folders into local files, for handing off | |||
| to external commands, or legacy tasks.</li> | |||
| <li>Refactor the replication mechanism out of ZipFileSystemProvder, | |||
| and make more general pluggable.</li> | |||
| <li>Capabilities discovery.</li> | |||
| <li>Attributes and attribute schema.</li> | |||
| <li>Handle file canonicalisation better (for cases like case-insensitive | |||
| file systems, symbolic links, name encoding, etc).</li> | |||
| <li>File system layering. That is, the ability for a file system to | |||
| sit on top of another file system, or a file from another file system | |||
| (e.g. Zip/Jar/Tar file systems, gzip/encoding file systems, virtual file | |||
| @@ -192,10 +199,6 @@ | |||
| <ul> | |||
| <li>Search through the code for 'TODO' items and fix them.</li> | |||
| <li>Tidy-up CLIMain so that it calls System.exit() with a non-zero exit code, | |||
| if the build fails.</li> | |||
| <li>Tidy-up the 'build failed' message, so that the stack trace is only | |||
| printed out if the log level is verbose/debug.</li> | |||
| <li>Allow service factories to be configured from the contents of the | |||
| <code>ant-services.xml</code> descriptor.</li> | |||
| <li>Route external process stdout and stderr through the logger.</li> | |||
| @@ -206,11 +209,10 @@ | |||
| <li>Fire ProjectListener events projectStarted() and projectFinished() | |||
| events on start and finish of referenced projects, adding indicator methods | |||
| to ProjectEvent.</li> | |||
| <li>Convert PropertyUtil to a non-static PropertyResolver service.</li> | |||
| <li>Validate project and target names in DefaultProjectBuilder - reject dodgy | |||
| names like "," or "", or " ". Probably want to exclude names that start or | |||
| names like "," or "", or " ". Probably want to reject names that start or | |||
| end with white-space (though internal whitespace is probably fine). We also | |||
| want to reserve certain punctuation characters like . , : ? [ ] { }, etc for | |||
| want to reserve certain punctuation characters like , : ? $ [ ] { } < >, etc for | |||
| future use.</li> | |||
| <li>Similarly, validate property names, using the same rules.</li> | |||
| <li>Detect duplicate type names.</li> | |||
| @@ -222,6 +224,7 @@ | |||
| an antlib.</li> | |||
| <li>Split up <code><is-set></code> condition into is-set and is-true conditions.</li> | |||
| <li>Allow the <code><if></code> task to take any condition implementation.</li> | |||
| <li>Add an else block to the <code><if></code> task.</li> | |||
| <li>Unit tests.</li> | |||
| </ul> | |||
| @@ -32,7 +32,8 @@ files are found in the <code>lib</code> directory:</p> | |||
| <tr> | |||
| <td>SMB VFS support (Samba, Windows shares)</td> | |||
| <td>jcifs.jar</td> | |||
| <td><a href="http://jcifs.samba.org">jcifs.samba.org</a></td> | |||
| <td><a href="http://jcifs.samba.org">jcifs.samba.org</a>. | |||
| <p>Note: there are problems using the 0.6.1 release. Try 0.6.0 instead.</p></td> | |||
| </tr> | |||
| <tr> | |||
| <td>FTP VFS support</td> | |||