Browse Source

VFS Tidy-ups:

* 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-ffa450edef68
master
adammurdoch 23 years ago
parent
commit
52926715b4
49 changed files with 1227 additions and 350 deletions
  1. +3
    -0
      proposal/myrmidon/build.xml
  2. +11
    -8
      proposal/myrmidon/docs/todo.html
  3. +2
    -1
      proposal/myrmidon/docs/user.html
  4. +1
    -1
      proposal/myrmidon/etc/testcases/org/apache/antlib/vfile/copy.ant
  5. +15
    -45
      proposal/myrmidon/src/java/org/apache/antlib/vfile/CopyFilesTask.java
  6. +9
    -0
      proposal/myrmidon/src/java/org/apache/antlib/vfile/DefaultFileSet.java
  7. +16
    -10
      proposal/myrmidon/src/java/org/apache/antlib/vfile/ListFileSetTask.java
  8. +0
    -1
      proposal/myrmidon/src/java/org/apache/antlib/vfile/Resources.properties
  9. +20
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java
  10. +39
    -3
      proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemManager.java
  11. +59
    -73
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileSystemManager.java
  12. +66
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultProviderContext.java
  13. +2
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/impl/Resources.properties
  14. +39
    -4
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileObject.java
  15. +62
    -14
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystemProvider.java
  16. +14
    -1
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProvider.java
  17. +12
    -1
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProviderContext.java
  18. +2
    -2
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ParsedUri.java
  19. +4
    -0
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties
  20. +154
    -19
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java
  21. +20
    -4
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileNameParser.java
  22. +12
    -7
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystemProvider.java
  23. +9
    -8
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java
  24. +13
    -8
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java
  25. +18
    -15
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileNameParser.java
  26. +12
    -7
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystemProvider.java
  27. +15
    -3
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ParsedZipUri.java
  28. +43
    -33
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileNameParser.java
  29. +66
    -12
      proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystemProvider.java
  30. +2
    -1
      proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/Resources.properties
  31. +108
    -0
      proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/VfsManager.java
  32. +1
    -2
      proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/VfsManagerFactory.java
  33. +1
    -6
      proposal/myrmidon/src/manifest/core-services.xml
  34. +108
    -5
      proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java
  35. +4
    -3
      proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractWritableFileSystemTest.java
  36. +11
    -5
      proposal/myrmidon/src/test/org/apache/aut/vfs/FtpFileSystemTest.java
  37. +4
    -6
      proposal/myrmidon/src/test/org/apache/aut/vfs/LocalFileSystemTest.java
  38. +41
    -0
      proposal/myrmidon/src/test/org/apache/aut/vfs/NestedZipFileSystemTest.java
  39. +9
    -4
      proposal/myrmidon/src/test/org/apache/aut/vfs/SmbFileSystemTest.java
  40. +5
    -3
      proposal/myrmidon/src/test/org/apache/aut/vfs/ZipFileSystemTest.java
  41. +108
    -5
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java
  42. +4
    -3
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractWritableFileSystemTest.java
  43. +11
    -5
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/FtpFileSystemTest.java
  44. +4
    -6
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/LocalFileSystemTest.java
  45. +41
    -0
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/NestedZipFileSystemTest.java
  46. +9
    -4
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/SmbFileSystemTest.java
  47. +5
    -3
      proposal/myrmidon/src/testcases/org/apache/aut/vfs/ZipFileSystemTest.java
  48. +11
    -8
      proposal/myrmidon/src/xdocs/todo.xml
  49. +2
    -1
      proposal/myrmidon/src/xdocs/user.xml

+ 3
- 0
proposal/myrmidon/build.xml View File

@@ -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"


+ 11
- 8
proposal/myrmidon/docs/todo.html View File

@@ -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 , : ? $ [ ] { } &lt; &gt;, 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>&lt;is-set&gt;</code> condition into is-set and is-true conditions.</li>
<li>Allow the <code>&lt;if&gt;</code> task to take any condition implementation.</li>
<li>Add an else block to the <code>&lt;if&gt;</code> task.</li>
<li>Unit tests.</li>
</ul>
</blockquote>


+ 2
- 1
proposal/myrmidon/docs/user.html View File

@@ -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
- 1
proposal/myrmidon/etc/testcases/org/apache/antlib/vfile/copy.ant View File

@@ -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>


+ 15
- 45
proposal/myrmidon/src/java/org/apache/antlib/vfile/CopyFilesTask.java View File

@@ -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 );
}
}
}

+ 9
- 0
proposal/myrmidon/src/java/org/apache/antlib/vfile/DefaultFileSet.java View File

@@ -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.
*/


+ 16
- 10
proposal/myrmidon/src/java/org/apache/antlib/vfile/ListFileSetTask.java View File

@@ -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 );
}
}
}
}

+ 0
- 1
proposal/myrmidon/src/java/org/apache/antlib/vfile/Resources.properties View 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.

+ 20
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/FileObject.java View File

@@ -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.
*


+ 39
- 3
proposal/myrmidon/src/java/org/apache/aut/vfs/FileSystemManager.java View File

@@ -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;
}

+ 59
- 73
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultFileSystemManager.java View File

@@ -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 );
}
}

+ 66
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/DefaultProviderContext.java View 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 );
}
}

+ 2
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/impl/Resources.properties View File

@@ -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}".

+ 39
- 4
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileObject.java View File

@@ -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();


+ 62
- 14
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/AbstractFileSystemProvider.java View File

@@ -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;
}

+ 14
- 1
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProvider.java View File

@@ -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;
}

+ 12
- 1
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/FileSystemProviderContext.java View File

@@ -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.
*/


+ 2
- 2
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ParsedUri.java View File

@@ -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;
}


+ 4
- 0
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/Resources.properties View File

@@ -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.

+ 154
- 19
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/UriParser.java View File

@@ -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;
}
}
}
}

+ 20
- 4
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileNameParser.java View File

@@ -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;
}
}

+ 12
- 7
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/ftp/FtpFileSystemProvider.java View File

@@ -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();


+ 9
- 8
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileNameParser.java View File

@@ -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;
}


+ 13
- 8
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/local/LocalFileSystemProvider.java View File

@@ -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 );
}
}

+ 18
- 15
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileNameParser.java View File

@@ -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;
}


+ 12
- 7
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/smb/SmbFileSystemProvider.java View File

@@ -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 );
}
}

+ 15
- 3
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ParsedZipUri.java View File

@@ -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;
}


+ 43
- 33
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileNameParser.java View File

@@ -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 );
}
}

+ 66
- 12
proposal/myrmidon/src/java/org/apache/aut/vfs/provider/zip/ZipFileSystemProvider.java View File

@@ -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 );
}

}

+ 2
- 1
proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/Resources.properties View File

@@ -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}".

+ 108
- 0
proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/VfsManager.java View File

@@ -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 );
}

}

+ 1
- 2
proposal/myrmidon/src/java/org/apache/myrmidon/framework/factories/VfsManagerFactory.java View File

@@ -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
- 6
proposal/myrmidon/src/manifest/core-services.xml View File

@@ -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>

+ 108
- 5
proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractFileSystemTest.java View File

@@ -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 )
{


+ 4
- 3
proposal/myrmidon/src/test/org/apache/aut/vfs/AbstractWritableFileSystemTest.java View File

@@ -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();


+ 11
- 5
proposal/myrmidon/src/test/org/apache/aut/vfs/FtpFileSystemTest.java View File

@@ -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 );
}
}

+ 4
- 6
proposal/myrmidon/src/test/org/apache/aut/vfs/LocalFileSystemTest.java View File

@@ -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 );
}

/**


+ 41
- 0
proposal/myrmidon/src/test/org/apache/aut/vfs/NestedZipFileSystemTest.java View File

@@ -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" );
}
}

+ 9
- 4
proposal/myrmidon/src/test/org/apache/aut/vfs/SmbFileSystemTest.java View File

@@ -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 );
}
}

+ 5
- 3
proposal/myrmidon/src/test/org/apache/aut/vfs/ZipFileSystemTest.java View File

@@ -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 );
}
}

+ 108
- 5
proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractFileSystemTest.java View File

@@ -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 )
{


+ 4
- 3
proposal/myrmidon/src/testcases/org/apache/aut/vfs/AbstractWritableFileSystemTest.java View File

@@ -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();


+ 11
- 5
proposal/myrmidon/src/testcases/org/apache/aut/vfs/FtpFileSystemTest.java View File

@@ -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 );
}
}

+ 4
- 6
proposal/myrmidon/src/testcases/org/apache/aut/vfs/LocalFileSystemTest.java View File

@@ -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 );
}

/**


+ 41
- 0
proposal/myrmidon/src/testcases/org/apache/aut/vfs/NestedZipFileSystemTest.java View File

@@ -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" );
}
}

+ 9
- 4
proposal/myrmidon/src/testcases/org/apache/aut/vfs/SmbFileSystemTest.java View File

@@ -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 );
}
}

+ 5
- 3
proposal/myrmidon/src/testcases/org/apache/aut/vfs/ZipFileSystemTest.java View File

@@ -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 );
}
}

+ 11
- 8
proposal/myrmidon/src/xdocs/todo.xml View File

@@ -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 , : ? $ [ ] { } &lt; &gt;, 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>&lt;is-set&gt;</code> condition into is-set and is-true conditions.</li>
<li>Allow the <code>&lt;if&gt;</code> task to take any condition implementation.</li>
<li>Add an else block to the <code>&lt;if&gt;</code> task.</li>
<li>Unit tests.</li>
</ul>



+ 2
- 1
proposal/myrmidon/src/xdocs/user.xml View File

@@ -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>


Loading…
Cancel
Save