You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@synapse.apache.org by ra...@apache.org on 2010/09/23 08:04:34 UTC

svn commit: r1000332 [8/27] - in /synapse/branches/commons-vfs-2-synapse-2.0: ./ core/ core/src/ core/src/main/ core/src/main/java/ core/src/main/java/org/ core/src/main/java/org/apache/ core/src/main/java/org/apache/commons/ core/src/main/java/org/apa...

Added: synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileObject.java
URL: http://svn.apache.org/viewvc/synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileObject.java?rev=1000332&view=auto
==============================================================================
--- synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileObject.java (added)
+++ synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileObject.java Thu Sep 23 06:04:21 2010
@@ -0,0 +1,1815 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs.provider;
+
+
+import org.apache.commons.vfs.operations.DefaultFileOperations;
+import org.apache.commons.vfs.operations.FileOperations;
+import org.apache.commons.vfs.util.FileObjectUtils;
+import org.apache.commons.vfs.util.RandomAccessMode;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.RandomAccessContent;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.Capability;
+import org.apache.commons.vfs.FileNotFolderException;
+import org.apache.commons.vfs.NameScope;
+import org.apache.commons.vfs.Selectors;
+import org.apache.commons.vfs.FileSelector;
+import org.apache.commons.vfs.FileContentInfoFactory;
+import org.apache.commons.vfs.FileUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileNotFoundException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A partial file object implementation.
+ *
+ * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
+ * @author Gary D. Gregory
+ * @version $Revision: 804548 $ $Date: 2009-08-16 07:42:32 +0530 (Sun, 16 Aug 2009) $
+ * @todo Chop this class up - move all the protected methods to several
+ * interfaces, so that structure and content can be separately overridden.
+ * @todo Check caps in methods like getChildren(), etc, and give better error messages
+ * (eg 'this file type does not support listing children', vs 'this is not a folder')
+ */
+public abstract class AbstractFileObject implements FileObject
+{
+    // private static final FileObject[] EMPTY_FILE_ARRAY = {};
+    private static final FileName[] EMPTY_FILE_ARRAY = {};
+
+    private static final int INITIAL_LISTSZ = 5;
+
+    private final AbstractFileName name;
+    private final AbstractFileSystem fs;
+
+    private FileContent content;
+
+    // Cached info
+    private boolean attached;
+    private FileType type;
+    private FileObject parent;
+
+    // Changed to hold only the name of the children and let the object
+    // go into the global files cache
+    // private FileObject[] children;
+    private FileName[] children;
+    private List objects;
+
+    /**
+     * FileServices instance.
+     */
+    private FileOperations operations;
+
+    protected AbstractFileObject(final FileName name,
+                                 final AbstractFileSystem fs)
+    {
+        this.name = (AbstractFileName) name;
+        this.fs = fs;
+        fs.fileObjectHanded(this);
+    }
+
+    /**
+     * Attaches this file object to its file resource.  This method is called
+     * before any of the doBlah() or onBlah() methods.  Sub-classes can use
+     * this method to perform lazy initialisation.
+     * <p/>
+     * This implementation does nothing.
+     */
+    protected void doAttach() throws Exception
+    {
+    }
+
+    /**
+     * Detaches this file object from its file resource.
+     * <p/>
+     * <p>Called when this file is closed.  Note that the file object may be
+     * reused later, so should be able to be reattached.
+     * <p/>
+     * This implementation does nothing.
+     */
+    protected void doDetach() throws Exception
+    {
+    }
+
+    /**
+     * Determines the type of this file.  Must not return null.  The return
+     * value of this method is cached, so the implementation can be expensive.
+     */
+    protected abstract FileType doGetType() throws Exception;
+
+    /**
+     * Determines if this file is hidden.  Is only called if {@link #doGetType}
+     * does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation always returns false.
+     */
+    protected boolean doIsHidden() throws Exception
+    {
+        return false;
+    }
+
+    /**
+     * Determines if this file can be read.  Is only called if {@link #doGetType}
+     * does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation always returns true.
+     */
+    protected boolean doIsReadable() throws Exception
+    {
+        return true;
+    }
+
+    /**
+     * Determines if this file can be written to.  Is only called if
+     * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation always returns true.
+     */
+    protected boolean doIsWriteable() throws Exception
+    {
+        return true;
+    }
+
+    /**
+     * Lists the children of this file.  Is only called if {@link #doGetType}
+     * returns {@link FileType#FOLDER}.  The return value of this method
+     * is cached, so the implementation can be expensive.<br />
+     * @return a possible empty String array if the file is a directory or null or an exception if the
+     * file is not a directory or can't be read
+     */
+    protected abstract String[] doListChildren() throws Exception;
+
+    /**
+     * Lists the children of this file.  Is only called if {@link #doGetType}
+     * returns {@link FileType#FOLDER}.  The return value of this method
+     * is cached, so the implementation can be expensive.<br>
+     * Other than <code>doListChildren</code> you could return FileObject's to e.g. reinitialize the
+     * type of the file.<br>
+     * (Introduced for Webdav: "permission denied on resource" during getType())
+     */
+    protected FileObject[] doListChildrenResolved() throws Exception
+    {
+        return null;
+    }
+
+    /**
+     * Deletes the file.  Is only called when:
+     * <ul>
+     * <li>{@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <li>{@link #doIsWriteable} returns true.
+     * <li>This file has no children, if a folder.
+     * </ul>
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected void doDelete() throws Exception
+    {
+        throw new FileSystemException("vfs.provider/delete-not-supported.error");
+    }
+
+    /**
+     * Renames the file.  Is only called when:
+     * <ul>
+     * <li>{@link #doIsWriteable} returns true.
+     * </ul>
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected void doRename(FileObject newfile) throws Exception
+    {
+        throw new FileSystemException("vfs.provider/rename-not-supported.error");
+    }
+
+    /**
+     * Creates this file as a folder.  Is only called when:
+     * <ul>
+     * <li>{@link #doGetType} returns {@link FileType#IMAGINARY}.
+     * <li>The parent folder exists and is writeable, or this file is the
+     * root of the file system.
+     * </ul>
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected void doCreateFolder() throws Exception
+    {
+        throw new FileSystemException("vfs.provider/create-folder-not-supported.error");
+    }
+
+    /**
+     * Called when the children of this file change.  Allows subclasses to
+     * refresh any cached information about the children of this file.
+     * <p/>
+     * This implementation does nothing.
+     */
+    protected void onChildrenChanged(FileName child, FileType newType) throws Exception
+    {
+    }
+
+    /**
+     * Called when the type or content of this file changes.
+     * <p/>
+     * This implementation does nothing.
+     */
+    protected void onChange() throws Exception
+    {
+    }
+
+    /**
+     * Returns the last modified time of this file.  Is only called if
+     * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected long doGetLastModifiedTime() throws Exception
+    {
+        throw new FileSystemException("vfs.provider/get-last-modified-not-supported.error");
+    }
+
+    /**
+     * Sets the last modified time of this file.  Is only called if
+     * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation throws an exception.
+     *
+     * @return false if it was not possible to change the time
+     */
+    protected boolean doSetLastModTime(final long modtime)
+        throws Exception
+    {
+        doSetLastModifiedTime(modtime);
+        return true;
+    }
+
+    /**
+     * Sets the last modified time of this file.  Is only called if
+     * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation throws an exception.
+     *
+     * @deprecated use {@link #doSetLastModTime}
+     */
+    protected void doSetLastModifiedTime(final long modtime)
+        throws Exception
+    {
+        throw new FileSystemException("vfs.provider/set-last-modified-not-supported.error");
+    }
+
+    /**
+     * Returns the attributes of this file.  Is only called if {@link #doGetType}
+     * does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation always returns an empty map.
+     */
+    protected Map doGetAttributes()
+        throws Exception
+    {
+        return Collections.EMPTY_MAP;
+    }
+
+    /**
+     * Sets an attribute of this file.  Is only called if {@link #doGetType}
+     * does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected void doSetAttribute(final String atttrName, final Object value)
+        throws Exception
+    {
+        throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
+    }
+
+    /**
+     * Removes an attribute of this file.  Is only called if {@link #doGetType}
+     * does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation throws an exception.
+     * @returns true if removing the attribute succeed. In this case we remove the attribute from
+     * our cache
+     */
+    protected void doRemoveAttribute(final String atttrName)
+        throws Exception
+    {
+        throw new FileSystemException("vfs.provider/remove-attribute-not-supported.error");
+    }
+
+    /**
+     * Returns the certificates used to sign this file.  Is only called if
+     * {@link #doGetType} does not return {@link FileType#IMAGINARY}.
+     * <p/>
+     * This implementation always returns null.
+     */
+    protected Certificate[] doGetCertificates() throws Exception
+    {
+        return null;
+    }
+
+    /**
+     * Returns the size of the file content (in bytes).  Is only called if
+     * {@link #doGetType} returns {@link FileType#FILE}.
+     */
+    protected abstract long doGetContentSize() throws Exception;
+
+    /**
+     * Creates an input stream to read the file content from.  Is only called
+     * if {@link #doGetType} returns {@link FileType#FILE}.
+     * <p/>
+     * <p>It is guaranteed that there are no open output streams for this file
+     * when this method is called.
+     * <p/>
+     * <p>The returned stream does not have to be buffered.
+     */
+    protected abstract InputStream doGetInputStream() throws Exception;
+
+    /**
+     * Creates access to the file for random i/o.  Is only called
+     * if {@link #doGetType} returns {@link FileType#FILE}.
+     * <p/>
+     * <p>It is guaranteed that there are no open output streams for this file
+     * when this method is called.
+     * <p/>
+     */
+    protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception
+    {
+        throw new FileSystemException("vfs.provider/random-access-not-supported.error");
+    }
+
+    /**
+     * Creates an output stream to write the file content to.  Is only
+     * called if:
+     * <ul>
+     * <li>{@link #doIsWriteable} returns true.
+     * <li>{@link #doGetType} returns {@link FileType#FILE}, or
+     * {@link #doGetType} returns {@link FileType#IMAGINARY}, and the file's
+     * parent exists and is a folder.
+     * </ul>
+     * <p/>
+     * <p>It is guaranteed that there are no open stream (input or output) for
+     * this file when this method is called.
+     * <p/>
+     * <p>The returned stream does not have to be buffered.
+     * <p/>
+     * This implementation throws an exception.
+     */
+    protected OutputStream doGetOutputStream(boolean bAppend) throws Exception
+    {
+        throw new FileSystemException("vfs.provider/write-not-supported.error");
+    }
+
+    /**
+     * Returns the URI of the file.
+     * @return The URI of the file.
+     */
+    public String toString()
+    {
+        return name.getURI();
+    }
+
+    /**
+     * Returns the name of the file.
+     * @return The FileName.
+     */
+    public FileName getName()
+    {
+        return name;
+    }
+
+    /**
+     * Returns the file system this file belongs to.
+     * @return The FileSystem this file is associated with.
+     */
+    public FileSystem getFileSystem()
+    {
+        return fs;
+    }
+
+    /**
+     * Returns a URL representation of the file.
+     * @return The URL representation of the file.
+     * @throws FileSystemException if an error occurs.
+     */
+    public URL getURL() throws FileSystemException
+    {
+        final StringBuffer buf = new StringBuffer();
+        try
+        {
+            return (URL) AccessController.doPrivileged(new PrivilegedExceptionAction()
+            {
+                public Object run() throws MalformedURLException
+                {
+                    return new URL(UriParser.extractScheme(name.getURI(), buf), "", -1,
+                        buf.toString(), new DefaultURLStreamHandler(fs.getContext(), fs.getFileSystemOptions()));
+                }
+            });
+        }
+        catch (final PrivilegedActionException e)
+        {
+            throw new FileSystemException("vfs.provider/get-url.error", name, e.getException());
+        }
+    }
+
+    /**
+     * Determines if the file exists.
+     * @return true if the file exists, false otherwise,
+     * @throws FileSystemException if an error occurs.
+     */
+    public boolean exists() throws FileSystemException
+    {
+        return getType() != FileType.IMAGINARY;
+    }
+
+    /**
+     * Returns the file's type.
+     * @return The FileType.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileType getType() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            attach();
+
+            // VFS-210: get the type only if requested for
+            try
+            {
+                if (type == null)
+                {
+                    setFileType(doGetType());
+                }
+                if (type == null)
+                {
+                    setFileType(FileType.IMAGINARY);
+                }
+            }
+            catch (Exception e)
+            {
+                throw new FileSystemException("vfs.provider/get-type.error", new Object[]{name}, e);
+            }
+
+            return type;
+        }
+    }
+
+    /**
+     * Determines if this file can be read.
+     * @return true if the file is a hidden file, false otherwise.
+     * @throws FileSystemException if an error occurs.
+     */
+    public boolean isHidden() throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doIsHidden();
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/check-is-hidden.error", name, exc);
+        }
+    }
+
+    /**
+     * Determines if this file can be read.
+     * @return true if the file can be read, false otherwise.
+     * @throws FileSystemException if an error occurs.
+     */
+    public boolean isReadable() throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doIsReadable();
+            }
+            else
+            {
+                return false;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/check-is-readable.error", name, exc);
+        }
+    }
+
+    /**
+     * Determines if this file can be written to.
+     * @return true if the file can be written to, false otherwise.
+     * @throws FileSystemException if an error occurs.
+     */
+    public boolean isWriteable() throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                return doIsWriteable();
+            }
+            else
+            {
+                final FileObject parent = getParent();
+                if (parent != null)
+                {
+                    return parent.isWriteable();
+                }
+                return true;
+            }
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/check-is-writeable.error", name, exc);
+        }
+    }
+
+    /**
+     * Returns the parent of the file.
+     * @return the parent FileObject.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject getParent() throws FileSystemException
+    {
+        if (this == fs.getRoot())
+        {
+            if (fs.getParentLayer() != null)
+            {
+                // Return the parent of the parent layer
+                return fs.getParentLayer().getParent();
+            }
+            else
+            {
+                // Root file has no parent
+                return null;
+            }
+        }
+
+        synchronized (fs)
+        {
+            // Locate the parent of this file
+            if (parent == null)
+            {
+                parent = fs.resolveFile(name.getParent());
+            }
+        }
+        return parent;
+    }
+
+    /**
+     * Returns the children of the file.
+     * @return an array of FileObjects, one per child.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject[] getChildren() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            // VFS-210
+            if (!getFileSystem().hasCapability(Capability.LIST_CHILDREN))
+            {
+                throw new FileNotFolderException(name);
+            }
+
+            /* VFS-210
+            if (!getType().hasChildren())
+            {
+                throw new FileSystemException("vfs.provider/list-children-not-folder.error", name);
+            }
+            */
+            attach();
+
+            // Use cached info, if present
+            if (children != null)
+            {
+                return resolveFiles(children);
+            }
+
+            // allow the filesystem to return resolved children. e.g. prefill type for webdav
+            FileObject[] childrenObjects;
+            try
+            {
+                childrenObjects = doListChildrenResolved();
+                children = extractNames(childrenObjects);
+            }
+            catch (FileSystemException exc)
+            {
+                // VFS-210
+                throw exc;
+            }
+            catch (Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/list-children.error", new Object[]{name}, exc);
+            }
+
+            if (childrenObjects != null)
+            {
+                return childrenObjects;
+            }
+
+            // List the children
+            final String[] files;
+            try
+            {
+                files = doListChildren();
+            }
+            catch (FileSystemException exc)
+            {
+                // VFS-210
+                throw exc;
+            }
+            catch (Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/list-children.error", new Object[]{name}, exc);
+            }
+
+            if (files == null)
+            {
+                // VFS-210
+                // honor the new doListChildren contract
+                // return null;
+                throw new FileNotFolderException(name);
+            }
+            else if (files.length == 0)
+            {
+                // No children
+                children = EMPTY_FILE_ARRAY;
+            }
+            else
+            {
+                // Create file objects for the children
+                // children = new FileObject[files.length];
+                children = new FileName[files.length];
+                for (int i = 0; i < files.length; i++)
+                {
+                    final String file = files[i];
+                    // children[i] = fs.resolveFile(name.resolveName(file, NameScope.CHILD));
+                    // children[i] = name.resolveName(file, NameScope.CHILD);
+                    children[i] = getFileSystem().getFileSystemManager().resolveName(name, file, NameScope.CHILD);
+                }
+            }
+
+            return resolveFiles(children);
+        }
+    }
+
+    private FileName[] extractNames(FileObject[] objects)
+    {
+        if (objects == null)
+        {
+            return null;
+        }
+
+        FileName[] names = new FileName[objects.length];
+        for (int iterObjects = 0; iterObjects < objects.length; iterObjects++)
+        {
+            names[iterObjects] = objects[iterObjects].getName();
+        }
+
+        return names;
+    }
+
+    private FileObject[] resolveFiles(FileName[] children) throws FileSystemException
+    {
+        if (children == null)
+        {
+            return null;
+        }
+
+        FileObject[] objects = new FileObject[children.length];
+        for (int iterChildren = 0; iterChildren < children.length; iterChildren++)
+        {
+            objects[iterChildren] = resolveFile(children[iterChildren]);
+        }
+
+        return objects;
+    }
+
+    private FileObject resolveFile(FileName child) throws FileSystemException
+    {
+        return fs.resolveFile(child);
+    }
+
+    /**
+     * Returns a child of this file.
+     * @param name The name of the child to locate.
+     * @return The FileObject for the file or null if the child does not exist.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject getChild(final String name) throws FileSystemException
+    {
+        // TODO - use a hashtable when there are a large number of children
+        FileObject[] children = getChildren();
+        for (int i = 0; i < children.length; i++)
+        {
+            // final FileObject child = children[i];
+            final FileName child = children[i].getName();
+            // TODO - use a comparator to compare names
+            // if (child.getName().getBaseName().equals(name))
+            if (child.getBaseName().equals(name))
+            {
+                return resolveFile(child);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a child by name.
+     * @param name The name of the child to locate.
+     * @param scope the NameScope.
+     * @return The FileObject for the file or null if the child does not exist.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject resolveFile(final String name, final NameScope scope)
+        throws FileSystemException
+    {
+        // return fs.resolveFile(this.name.resolveName(name, scope));
+        return fs.resolveFile(getFileSystem().getFileSystemManager().resolveName(this.name, name, scope));
+    }
+
+    /**
+     * Finds a file, relative to this file.
+     *
+     * @param path The path of the file to locate.  Can either be a relative
+     *             path, which is resolved relative to this file, or an
+     *             absolute path, which is resolved relative to the file system
+     *             that contains this file.
+     * @return The FileObject.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject resolveFile(final String path) throws FileSystemException
+    {
+        final FileName otherName = getFileSystem().getFileSystemManager().resolveName(name, path);
+        return fs.resolveFile(otherName);
+    }
+
+    /**
+     * Deletes this file, once all its children have been deleted
+     *
+     * @return true if this file has been deleted
+     */
+    private boolean deleteSelf() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            /* Its possible to delete a read-only file if you have write-execute access to the directory
+            if (!isWriteable())
+            {
+                throw new FileSystemException("vfs.provider/delete-read-only.error", name);
+            }
+            */
+
+            /* VFS-210
+            if (getType() == FileType.IMAGINARY)
+            {
+                // File does not exist
+                return false;
+            }
+            */
+
+            try
+            {
+                // Delete the file
+                doDelete();
+
+                // Update cached info
+                handleDelete();
+            }
+            catch (final RuntimeException re)
+            {
+                throw re;
+            }
+            catch (final Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/delete.error", new Object[]{name}, exc);
+            }
+
+            return true;
+        }
+    }
+
+    /**
+     * Deletes this file.
+     *
+     * @return true if this object has been deleted
+     * @todo This will not fail if this is a non-empty folder.
+     * @throws FileSystemException if an error occurs.
+     */
+    public boolean delete() throws FileSystemException
+    {
+        return delete(Selectors.SELECT_SELF) > 0;
+    }
+
+    /**
+     * Deletes this file, and all children.
+     *
+     * @param selector The FileSelector.
+     * @return the number of deleted files.
+     * @throws FileSystemException if an error occurs.
+     */
+    public int delete(final FileSelector selector) throws FileSystemException
+    {
+        int nuofDeleted = 0;
+
+        /* VFS-210
+        if (getType() == FileType.IMAGINARY)
+        {
+            // File does not exist
+            return nuofDeleted;
+        }
+        */
+
+        // Locate all the files to delete
+        ArrayList files = new ArrayList();
+        findFiles(selector, true, files);
+
+        // Delete 'em
+        final int count = files.size();
+        for (int i = 0; i < count; i++)
+        {
+            final AbstractFileObject file = FileObjectUtils.getAbstractFileObject((FileObject) files.get(i));
+            // file.attach();
+
+            // VFS-210: It seems impossible to me that findFiles will return a list with hidden files/directories
+            // in it, else it would not be hidden. Checking for the file-type seems ok in this case
+            // If the file is a folder, make sure all its children have been deleted
+            if (file.getType().hasChildren() && file.getChildren().length != 0)
+            {
+                // Skip - as the selector forced us not to delete all files
+                continue;
+            }
+
+            // Delete the file
+            boolean deleted = file.deleteSelf();
+            if (deleted)
+            {
+                nuofDeleted++;
+            }
+        }
+
+        return nuofDeleted;
+    }
+
+    /**
+     * Creates this file, if it does not exist.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void createFile() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            try
+            {
+                // VFS-210: We do not want to trunc any existing file, checking for its existence is
+                // still required
+                if (exists() && !FileType.FILE.equals(getType()))
+                {
+                    throw new FileSystemException("vfs.provider/create-file.error", name);
+                }
+
+                if (!exists())
+                {
+                    getOutputStream().close();
+                    endOutput();
+                }
+            }
+            catch (final RuntimeException re)
+            {
+                throw re;
+            }
+            catch (final Exception e)
+            {
+                throw new FileSystemException("vfs.provider/create-file.error", name, e);
+            }
+        }
+    }
+
+    /**
+     * Creates this folder, if it does not exist.  Also creates any ancestor
+     * files which do not exist.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void createFolder() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            // VFS-210: we create a folder only if it does not already exist. So this check should be safe.
+            if (getType().hasChildren())
+            {
+                // Already exists as correct type
+                return;
+            }
+            if (getType() != FileType.IMAGINARY)
+            {
+                throw new FileSystemException("vfs.provider/create-folder-mismatched-type.error", name);
+            }
+            /* VFS-210: checking for writeable is not always possible as the security constraint might
+               be more complex
+            if (!isWriteable())
+            {
+                throw new FileSystemException("vfs.provider/create-folder-read-only.error", name);
+            }
+            */
+
+            // Traverse up the heirarchy and make sure everything is a folder
+            final FileObject parent = getParent();
+            if (parent != null)
+            {
+                parent.createFolder();
+            }
+
+            try
+            {
+                // Create the folder
+                doCreateFolder();
+
+                // Update cached info
+                handleCreate(FileType.FOLDER);
+            }
+            catch (final RuntimeException re)
+            {
+                throw re;
+            }
+            catch (final Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/create-folder.error", name, exc);
+            }
+        }
+    }
+
+    /**
+     * Copies another file to this file.
+     * @param file The FileObject to copy.
+     * @param selector The FileSelector.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void copyFrom(final FileObject file, final FileSelector selector)
+        throws FileSystemException
+    {
+        if (!file.exists())
+        {
+            throw new FileSystemException("vfs.provider/copy-missing-file.error", file);
+        }
+        /* we do not alway know if a file is writeable
+        if (!isWriteable())
+        {
+            throw new FileSystemException("vfs.provider/copy-read-only.error", new Object[]{file.getType(),
+            file.getName(), this}, null);
+        }
+        */
+
+        // Locate the files to copy across
+        final ArrayList files = new ArrayList();
+        file.findFiles(selector, false, files);
+
+        // Copy everything across
+        final int count = files.size();
+        for (int i = 0; i < count; i++)
+        {
+            final FileObject srcFile = (FileObject) files.get(i);
+
+            // Determine the destination file
+            final String relPath = file.getName().getRelativeName(srcFile.getName());
+            final FileObject destFile = resolveFile(relPath, NameScope.DESCENDENT_OR_SELF);
+
+            // Clean up the destination file, if necessary
+            if (destFile.exists() && destFile.getType() != srcFile.getType())
+            {
+                // The destination file exists, and is not of the same type,
+                // so delete it
+                // TODO - add a pluggable policy for deleting and overwriting existing files
+                destFile.delete(Selectors.SELECT_ALL);
+            }
+
+            // Copy across
+            try
+            {
+                if (srcFile.getType().hasContent())
+                {
+                    FileUtil.copyContent(srcFile, destFile);
+                }
+                else if (srcFile.getType().hasChildren())
+                {
+                    destFile.createFolder();
+                }
+            }
+            catch (final IOException e)
+            {
+                throw new FileSystemException("vfs.provider/copy-file.error", new Object[]{srcFile, destFile}, e);
+            }
+        }
+    }
+
+    /**
+     * Moves (rename) the file to another one.
+     * @param destFile The target FileObject.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void moveTo(FileObject destFile) throws FileSystemException
+    {
+        if (canRenameTo(destFile))
+        {
+            if (!getParent().isWriteable())
+            {
+                throw new FileSystemException("vfs.provider/rename-parent-read-only.error", new FileName[]{getName(),
+                        getParent().getName()});
+            }
+        }
+        else
+        {
+            if (!isWriteable())
+            {
+                throw new FileSystemException("vfs.provider/rename-read-only.error", getName());
+            }
+        }
+
+        if (destFile.exists() && !isSameFile(destFile))
+        {
+            destFile.delete(Selectors.SELECT_ALL);
+            // throw new FileSystemException("vfs.provider/rename-dest-exists.error", destFile.getName());
+        }
+
+        if (canRenameTo(destFile))
+        {
+            // issue rename on same filesystem
+            try
+            {
+                attach();
+                doRename(destFile);
+
+                (FileObjectUtils.getAbstractFileObject(destFile)).handleCreate(getType());
+
+                destFile.close(); // now the destFile is no longer imaginary. force reattach.
+
+                handleDelete(); // fire delete-events. This file-object (src) is like deleted.
+            }
+            catch (final RuntimeException re)
+            {
+                throw re;
+            }
+            catch (final Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/rename.error", new Object[]
+                    {
+                        getName(),
+                        destFile.getName()
+                    }, exc);
+            }
+        }
+        else
+        {
+            // different fs - do the copy/delete stuff
+
+            destFile.copyFrom(this, Selectors.SELECT_SELF);
+
+            if (((destFile.getType().hasContent()
+                    && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FILE))
+                  || (destFile.getType().hasChildren()
+                    && destFile.getFileSystem().hasCapability(Capability.SET_LAST_MODIFIED_FOLDER)))
+                    && getFileSystem().hasCapability(Capability.GET_LAST_MODIFIED))
+            {
+                destFile.getContent().setLastModifiedTime(this.getContent().getLastModifiedTime());
+            }
+
+            deleteSelf();
+        }
+
+    }
+
+    /**
+     * Checks if this fileObject is the same file as <code>destFile</code> just with a different
+     * name.<br />
+     * E.g. for case insensitive filesystems like windows.
+     */
+    protected boolean isSameFile(FileObject destFile) throws FileSystemException
+    {
+        attach();
+        return doIsSameFile(destFile);
+    }
+
+    /**
+     * Checks if this fileObject is the same file as <code>destFile</code> just with a different
+     * name.<br />
+     * E.g. for case insensitive filesystems like windows.
+     */
+    protected boolean doIsSameFile(FileObject destFile) throws FileSystemException
+    {
+        return false;
+    }
+
+    /**
+     * Queries the object if a simple rename to the filename of <code>newfile</code>
+     * is possible.
+     *
+     * @param newfile the new filename
+     * @return true if rename is possible
+     */
+    public boolean canRenameTo(FileObject newfile)
+    {
+        if (getFileSystem() == newfile.getFileSystem())
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Finds the set of matching descendents of this file, in depthwise
+     * order.
+     *
+     * @param selector The FileSelector.
+     * @return list of files or null if the base file (this object) do not exist
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject[] findFiles(final FileSelector selector) throws FileSystemException
+    {
+        if (!exists())
+        {
+            return null;
+        }
+
+        final ArrayList list = new ArrayList();
+        findFiles(selector, true, list);
+        return (FileObject[]) list.toArray(new FileObject[list.size()]);
+    }
+
+    /**
+     * Returns the file's content.
+     * @return the FileContent for this FileObject.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileContent getContent() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            attach();
+            if (content == null)
+            {
+                content = doCreateFileContent();
+            }
+            return content;
+        }
+    }
+
+    /**
+     * Create a FileContent implementation.
+     */
+    protected FileContent doCreateFileContent() throws FileSystemException
+    {
+        return new DefaultFileContent(this, getFileContentInfoFactory());
+    }
+
+    /**
+     * This will prepare the fileObject to get resynchronized with the underlaying filesystem if required.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void refresh() throws FileSystemException
+    {
+        // Detach from the file
+        try
+        {
+            detach();
+        }
+        catch (final Exception e)
+        {
+            throw new FileSystemException("vfs.provider/resync.error", name, e);
+        }
+    }
+
+    /**
+     * Closes this file, and its content.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void close() throws FileSystemException
+    {
+        FileSystemException exc = null;
+
+        // Close the content
+        if (content != null)
+        {
+            try
+            {
+                content.close();
+                content = null;
+            }
+            catch (FileSystemException e)
+            {
+                exc = e;
+            }
+        }
+
+        // Detach from the file
+        try
+        {
+            detach();
+        }
+        catch (final Exception e)
+        {
+            exc = new FileSystemException("vfs.provider/close.error", name, e);
+        }
+
+        if (exc != null)
+        {
+            throw exc;
+        }
+    }
+
+    /**
+     * Returns an input stream to use to read the content of the file.
+     * @return The InputStream to access this file's content.
+     * @throws FileSystemException if an error occurs.
+     */
+    public InputStream getInputStream() throws FileSystemException
+    {
+        /* VFS-210
+        if (!getType().hasContent())
+        {
+            throw new FileSystemException("vfs.provider/read-not-file.error", name);
+        }
+        if (!isReadable())
+        {
+            throw new FileSystemException("vfs.provider/read-not-readable.error", name);
+        }
+        */
+
+        // Get the raw input stream
+        try
+        {
+            return doGetInputStream();
+        }
+        catch (final org.apache.commons.vfs.FileNotFoundException exc)
+        {
+            throw new org.apache.commons.vfs.FileNotFoundException(name, exc);
+        }
+        catch (final FileNotFoundException exc)
+        {
+            throw new org.apache.commons.vfs.FileNotFoundException(name, exc);
+        }
+        catch (final FileSystemException exc)
+        {
+            throw exc;
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/read.error", name, exc);
+        }
+    }
+
+    /**
+     * Returns an input/output stream to use to read and write the content of the file in and
+     * random manner.
+     * @param mode The RandomAccessMode.
+     * @return The RandomAccessContent.
+     * @throws FileSystemException if an error occurs.
+     */
+    public RandomAccessContent getRandomAccessContent(final RandomAccessMode mode) throws FileSystemException
+    {
+        /* VFS-210
+        if (!getType().hasContent())
+        {
+            throw new FileSystemException("vfs.provider/read-not-file.error", name);
+        }
+        */
+
+        if (mode.requestRead())
+        {
+            if (!getFileSystem().hasCapability(Capability.RANDOM_ACCESS_READ))
+            {
+                throw new FileSystemException("vfs.provider/random-access-read-not-supported.error");
+            }
+            if (!isReadable())
+            {
+                throw new FileSystemException("vfs.provider/read-not-readable.error", name);
+            }
+        }
+
+        if (mode.requestWrite())
+        {
+            if (!getFileSystem().hasCapability(Capability.RANDOM_ACCESS_WRITE))
+            {
+                throw new FileSystemException("vfs.provider/random-access-write-not-supported.error");
+            }
+            if (!isWriteable())
+            {
+                throw new FileSystemException("vfs.provider/write-read-only.error", name);
+            }
+        }
+
+        // Get the raw input stream
+        try
+        {
+            return doGetRandomAccessContent(mode);
+        }
+        catch (final Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/random-access.error", name, exc);
+        }
+    }
+
+    /**
+     * Prepares this file for writing.  Makes sure it is either a file,
+     * or its parent folder exists.  Returns an output stream to use to
+     * write the content of the file to.
+     * @return An OutputStream where the new contents of the file can be written.
+     * @throws FileSystemException if an error occurs.
+     */
+    public OutputStream getOutputStream() throws FileSystemException
+    {
+        return getOutputStream(false);
+    }
+
+    /**
+     * Prepares this file for writing.  Makes sure it is either a file,
+     * or its parent folder exists.  Returns an output stream to use to
+     * write the content of the file to.<br>
+     *
+     * @param bAppend true when append to the file.<br>
+     *                Note: If the underlaying filesystem do not support this, it wont work.
+     * @return An OutputStream where the new contents of the file can be written.
+     * @throws FileSystemException if an error occurs.
+     */
+    public OutputStream getOutputStream(boolean bAppend) throws FileSystemException
+    {
+        /* VFS-210
+        if (getType() != FileType.IMAGINARY && !getType().hasContent())
+        {
+            throw new FileSystemException("vfs.provider/write-not-file.error", name);
+        }
+        if (!isWriteable())
+        {
+            throw new FileSystemException("vfs.provider/write-read-only.error", name);
+        }
+        */
+
+        if (bAppend && !getFileSystem().hasCapability(Capability.APPEND_CONTENT))
+        {
+            throw new FileSystemException("vfs.provider/write-append-not-supported.error", name);
+        }
+
+        if (getType() == FileType.IMAGINARY)
+        {
+// Does not exist - make sure parent does
+            FileObject parent = getParent();
+            if (parent != null)
+            {
+                parent.createFolder();
+            }
+        }
+
+// Get the raw output stream
+        try
+        {
+            return doGetOutputStream(bAppend);
+        }
+        catch (RuntimeException re)
+        {
+            throw re;
+        }
+        catch (Exception exc)
+        {
+            throw new FileSystemException("vfs.provider/write.error", new Object[]{name}, exc);
+        }
+    }
+
+    /**
+     * Detaches this file, invaliating all cached info.  This will force
+     * a call to {@link #doAttach} next time this file is used.
+     */
+    private void detach() throws Exception
+    {
+        synchronized (fs)
+        {
+            if (attached)
+            {
+                try
+                {
+                    doDetach();
+                }
+                finally
+                {
+                    attached = false;
+                    setFileType(null);
+                    parent = null;
+
+                    // fs.fileDetached(this);
+
+                    removeChildrenCache();
+                    // children = null;
+                }
+            }
+        }
+    }
+
+    private void removeChildrenCache()
+    {
+        /*
+        if (children != null)
+        {
+            for (int iterChildren = 0; iterChildren < children.length; iterChildren++)
+            {
+                fs.removeFileFromCache(children[iterChildren].getName());
+            }
+
+            children = null;
+        }
+        */
+        children = null;
+    }
+
+    /**
+     * Attaches to the file.
+     */
+    private void attach() throws FileSystemException
+    {
+        synchronized (fs)
+        {
+            if (attached)
+            {
+                return;
+            }
+
+            try
+            {
+                // Attach and determine the file type
+                doAttach();
+                attached = true;
+                // now the type could already be injected by doAttach (e.g from parent to child)
+
+                /* VFS-210: determine the type when really asked fore
+                if (type == null)
+                {
+                    setFileType(doGetType());
+                }
+                if (type == null)
+                {
+                    setFileType(FileType.IMAGINARY);
+                }
+                */
+            }
+            catch (Exception exc)
+            {
+                throw new FileSystemException("vfs.provider/get-type.error", new Object[]{name}, exc);
+            }
+
+            // fs.fileAttached(this);
+        }
+    }
+
+    /**
+     * Called when the ouput stream for this file is closed.
+     */
+    protected void endOutput() throws Exception
+    {
+        if (getType() == FileType.IMAGINARY)
+        {
+            // File was created
+            handleCreate(FileType.FILE);
+        }
+        else
+        {
+            // File has changed
+            onChange();
+        }
+    }
+
+    /**
+     * Called when this file is created.  Updates cached info and notifies
+     * the parent and file system.
+     */
+    protected void handleCreate(final FileType newType) throws Exception
+    {
+        synchronized (fs)
+        {
+            if (attached)
+            {
+                // Fix up state
+                injectType(newType);
+
+                removeChildrenCache();
+                // children = EMPTY_FILE_ARRAY;
+
+                // Notify subclass
+                onChange();
+            }
+
+            // Notify parent that its child list may no longer be valid
+            notifyParent(this.getName(), newType);
+
+            // Notify the file system
+            fs.fireFileCreated(this);
+        }
+    }
+
+    /**
+     * Called when this file is deleted.  Updates cached info and notifies
+     * subclasses, parent and file system.
+     */
+    protected void handleDelete() throws Exception
+    {
+        synchronized (fs)
+        {
+            if (attached)
+            {
+                // Fix up state
+                injectType(FileType.IMAGINARY);
+                removeChildrenCache();
+                // children = null;
+
+                // Notify subclass
+                onChange();
+            }
+
+            // Notify parent that its child list may no longer be valid
+            notifyParent(this.getName(), FileType.IMAGINARY);
+
+            // Notify the file system
+            fs.fireFileDeleted(this);
+        }
+    }
+
+    /**
+     * Called when this file is changed.<br />
+     * This will only happen if you monitor the file using {@link org.apache.commons.vfs.FileMonitor}.
+     */
+    protected void handleChanged() throws Exception
+    {
+        // Notify the file system
+        fs.fireFileChanged(this);
+    }
+
+    /**
+     * Notifies the file that its children have changed.
+     *
+     * @deprecated use {@link #childrenChanged(FileName,FileType)}
+     */
+    protected void childrenChanged() throws Exception
+    {
+        childrenChanged(null, null);
+    }
+
+    /**
+     * Notifies the file that its children have changed.
+     */
+    protected void childrenChanged(FileName childName, FileType newType) throws Exception
+    {
+        // TODO - this may be called when not attached
+
+        if (children != null)
+        {
+            if (childName != null && newType != null)
+            {
+                // TODO - figure out if children[] can be replaced by list
+                ArrayList list = new ArrayList(Arrays.asList(children));
+                if (newType.equals(FileType.IMAGINARY))
+                {
+                    list.remove(childName);
+                }
+                else
+                {
+                    list.add(childName);
+                }
+                children = new FileName[list.size()];
+                list.toArray(children);
+            }
+        }
+
+        // removeChildrenCache();
+        onChildrenChanged(childName, newType);
+    }
+
+    /**
+     * Notify the parent of a change to its children, when a child is created
+     * or deleted.
+     */
+    private void notifyParent(FileName childName, FileType newType) throws Exception
+    {
+        if (parent == null)
+        {
+            FileName parentName = name.getParent();
+            if (parentName != null)
+            {
+                // Locate the parent, if it is cached
+                parent = fs.getFileFromCache(parentName);
+            }
+        }
+
+        if (parent != null)
+        {
+            FileObjectUtils.getAbstractFileObject(parent).childrenChanged(childName, newType);
+        }
+    }
+
+    /**
+     * Traverses the descendents of this file, and builds a list of selected
+     * files.
+     * @param selector The FileSelector.
+     * @param depthwise if true files are added after their descendants, before otherwise.
+     * @param selected A List of the located FileObjects.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void findFiles(final FileSelector selector,
+                          final boolean depthwise,
+                          final List selected) throws FileSystemException
+    {
+        try
+        {
+            if (exists())
+            {
+                // Traverse starting at this file
+                final DefaultFileSelectorInfo info = new DefaultFileSelectorInfo();
+                info.setBaseFolder(this);
+                info.setDepth(0);
+                info.setFile(this);
+                traverse(info, selector, depthwise, selected);
+            }
+        }
+        catch (final Exception e)
+        {
+            throw new FileSystemException("vfs.provider/find-files.error", name, e);
+        }
+    }
+
+    /**
+     * Traverses a file.
+     */
+    private static void traverse(final DefaultFileSelectorInfo fileInfo,
+                                 final FileSelector selector,
+                                 final boolean depthwise,
+                                 final List selected)
+        throws Exception
+    {
+        // Check the file itself
+        final FileObject file = fileInfo.getFile();
+        final int index = selected.size();
+
+        // If the file is a folder, traverse it
+        if (file.getType().hasChildren() && selector.traverseDescendents(fileInfo))
+        {
+            final int curDepth = fileInfo.getDepth();
+            fileInfo.setDepth(curDepth + 1);
+
+            // Traverse the children
+            final FileObject[] children = file.getChildren();
+            for (int i = 0; i < children.length; i++)
+            {
+                final FileObject child = children[i];
+                fileInfo.setFile(child);
+                traverse(fileInfo, selector, depthwise, selected);
+            }
+
+            fileInfo.setFile(file);
+            fileInfo.setDepth(curDepth);
+        }
+
+        // Add the file if doing depthwise traversal
+        if (selector.includeFile(fileInfo))
+        {
+            if (depthwise)
+            {
+                // Add this file after its descendents
+                selected.add(file);
+            }
+            else
+            {
+                // Add this file before its descendents
+                selected.add(index, file);
+            }
+        }
+    }
+
+    /**
+     * Check if the content stream is open.
+     *
+     * @return true if this is the case
+     */
+    public boolean isContentOpen()
+    {
+        if (content == null)
+        {
+            return false;
+        }
+
+        return content.isOpen();
+    }
+
+    /**
+     * Check if the internal state is "attached".
+     *
+     * @return true if this is the case
+     */
+    public boolean isAttached()
+    {
+        return attached;
+    }
+
+    /**
+     * create the filecontentinfo implementation
+     */
+    protected FileContentInfoFactory getFileContentInfoFactory()
+    {
+        return getFileSystem().getFileSystemManager().getFileContentInfoFactory();
+    }
+
+    protected void injectType(FileType fileType)
+    {
+        setFileType(fileType);
+    }
+
+    private void setFileType(FileType type)
+    {
+        if (type != null && type != FileType.IMAGINARY)
+        {
+            try
+            {
+                name.setType(type);
+            }
+            catch (FileSystemException e)
+            {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        this.type = type;
+    }
+
+    /**
+     * This method is meant to add an object where this object holds a strong reference then.
+     * E.g. a archive-filesystem creates a list of all childs and they shouldnt get
+     * garbage collected until the container is garbage collected
+     *
+     * @param strongRef The Object to add.
+     */
+    public void holdObject(Object strongRef)
+    {
+        if (objects == null)
+        {
+            objects = new ArrayList(INITIAL_LISTSZ);
+        }
+        objects.add(strongRef);
+    }
+
+    /**
+     * will be called after this file-object closed all its streams.
+     */
+    protected void notifyAllStreamsClosed()
+    {
+    }
+
+    // --- OPERATIONS ---
+
+    /**
+     * @return FileOperations interface that provides access to the operations
+     *         API.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileOperations getFileOperations() throws FileSystemException
+    {
+        if (operations == null)
+        {
+            operations = new DefaultFileOperations(this);
+        }
+
+        return operations;
+    }
+
+    protected void finalize() throws Throwable
+    {
+        fs.fileObjectDestroyed(this);
+
+        super.finalize();
+    }
+}

Added: synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileProvider.java
URL: http://svn.apache.org/viewvc/synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileProvider.java?rev=1000332&view=auto
==============================================================================
--- synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileProvider.java (added)
+++ synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileProvider.java Thu Sep 23 06:04:21 2010
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs.provider;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemConfigBuilder;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.local.GenericFileNameParser;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A partial {@link FileProvider} implementation.  Takes care of managing the
+ * file systems created by the provider.
+ *
+ * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
+ * @version $Revision: 804273 $ $Date: 2009-08-14 21:26:52 +0530 (Fri, 14 Aug 2009) $
+ */
+public abstract class AbstractFileProvider
+    extends AbstractVfsContainer
+    implements FileProvider
+{
+    /**
+     * The cached file systems.  This is a mapping from root URI to
+     * FileSystem object.
+     */
+    // private final Map fileSystems = new HashMap();
+    private final Map fileSystems = new TreeMap();
+
+    private FileNameParser parser;
+
+    public AbstractFileProvider()
+    {
+        parser = GenericFileNameParser.getInstance();
+    }
+
+    protected FileNameParser getFileNameParser()
+    {
+        return parser;
+    }
+
+    protected void setFileNameParser(FileNameParser parser)
+    {
+        this.parser = parser;
+    }
+
+    /**
+     * Closes the file systems created by this provider.
+     */
+    public void close()
+    {
+        synchronized (this)
+        {
+            fileSystems.clear();
+        }
+
+        super.close();
+    }
+
+    /**
+     * Creates a layered file system.  This method throws a 'not supported' exception.
+     * @param scheme The protocol to use to access the file.
+     * @param file a FileObject.
+     * @param properties Options to the file system.
+     * @return A FileObject associated with the new FileSystem.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject createFileSystem(final String scheme, final FileObject file, final FileSystemOptions properties)
+        throws FileSystemException
+    {
+        // Can't create a layered file system
+        throw new FileSystemException("vfs.provider/not-layered-fs.error", scheme);
+    }
+
+    /**
+     * Adds a file system to those cached by this provider.  The file system
+     * may implement {@link VfsComponent}, in which case it is initialised.
+     */
+    protected void addFileSystem(final Comparable key, final FileSystem fs)
+        throws FileSystemException
+    {
+        // Add to the cache
+        addComponent(fs);
+
+        FileSystemKey treeKey = new FileSystemKey(key, fs.getFileSystemOptions());
+        ((AbstractFileSystem) fs).setCacheKey(treeKey);
+
+        synchronized (this)
+        {
+            fileSystems.put(treeKey, fs);
+        }
+    }
+
+    /**
+     * Locates a cached file system
+     *
+     * @return The provider, or null if it is not cached.
+     */
+    protected FileSystem findFileSystem(final Comparable key, final FileSystemOptions fileSystemProps)
+    {
+        FileSystemKey treeKey = new FileSystemKey(key, fileSystemProps);
+
+        synchronized (this)
+        {
+            return (FileSystem) fileSystems.get(treeKey);
+        }
+    }
+
+    /**
+     * Returns the FileSystemConfigBuidler.
+     * @return the FileSystemConfigBuilder.
+     */
+    public FileSystemConfigBuilder getConfigBuilder()
+    {
+        return null;
+    }
+
+    /**
+     * Free unused resources.
+     */
+    public void freeUnusedResources()
+    {
+        Object[] item;
+        synchronized (this)
+        {
+            item = fileSystems.values().toArray();
+        }
+        for (int i = 0; i < item.length; ++i)
+        {
+            AbstractFileSystem fs = (AbstractFileSystem) item[i];
+            if (fs.isReleaseable())
+            {
+                fs.closeCommunicationLink();
+            }
+        }
+    }
+
+    /**
+     * Close the FileSystem.
+     * @param filesystem The FileSystem to close.
+     */
+    public void closeFileSystem(final FileSystem filesystem)
+    {
+        AbstractFileSystem fs = (AbstractFileSystem) filesystem;
+
+        synchronized (this)
+        {
+            if (fs.getCacheKey() != null)
+            {
+                fileSystems.remove(fs.getCacheKey());
+            }
+        }
+
+        removeComponent(fs);
+        fs.close();
+    }
+
+    /**
+     * Parses an absolute URI.
+     *
+     * @param base The base file - if null the <code>uri</code> needs to be absolute
+     * @param uri The URI to parse.
+     * @return The FileName.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileName parseUri(FileName base, String uri) throws FileSystemException
+    {
+        if (getFileNameParser() != null)
+        {
+            return getFileNameParser().parseUri(getContext(), base, uri);
+        }
+
+        throw new FileSystemException("vfs.provider/filename-parser-missing.error");
+        // return GenericFileName.parseUri(getFileNameParser(), uri, 0);
+    }
+}

Added: synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileSystem.java
URL: http://svn.apache.org/viewvc/synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileSystem.java?rev=1000332&view=auto
==============================================================================
--- synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileSystem.java (added)
+++ synapse/branches/commons-vfs-2-synapse-2.0/core/src/main/java/org/apache/commons/vfs/provider/AbstractFileSystem.java Thu Sep 23 06:04:21 2010
@@ -0,0 +1,686 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.vfs.provider;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.vfs.CacheStrategy;
+import org.apache.commons.vfs.Capability;
+import org.apache.commons.vfs.FileListener;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelector;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.FilesCache;
+import org.apache.commons.vfs.VfsLog;
+import org.apache.commons.vfs.FileSystemConfigBuilder;
+import org.apache.commons.vfs.impl.DefaultFileSystemConfigBuilder;
+import org.apache.commons.vfs.cache.OnCallRefreshFileObject;
+import org.apache.commons.vfs.events.AbstractFileChangeEvent;
+import org.apache.commons.vfs.events.ChangedEvent;
+import org.apache.commons.vfs.events.CreateEvent;
+import org.apache.commons.vfs.events.DeleteEvent;
+import org.apache.commons.vfs.util.Messages;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A partial {@link org.apache.commons.vfs.FileSystem} implementation.
+ *
+ * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
+ * @version $Revision: 833538 $ $Date: 2009-11-07 01:26:25 +0530 (Sat, 07 Nov 2009) $
+ */
+public abstract class AbstractFileSystem
+    extends AbstractVfsComponent
+    implements FileSystem
+{
+    private static final Log LOG = LogFactory.getLog(AbstractFileSystem.class);
+
+    /**
+     * The "root" of the file system. This is always "/" so it isn't always the "real"
+     * root.
+     */
+    private final FileName rootName;
+
+    /**
+     * The root URI of the file system. The base path specified as a file system option
+     * when the file system was created.
+     */
+    private final String rootURI;
+
+    private FileObject parentLayer;
+    // private FileObject root;
+    private final Collection caps = new HashSet();
+
+    /**
+     * Map from FileName to FileObject.
+     */
+    // private FilesCache files;
+
+    /**
+     * Map from FileName to an ArrayList of listeners for that file.
+     */
+    private final Map listenerMap = new HashMap();
+
+    /**
+     * FileSystemOptions used for configuration
+     */
+    private final FileSystemOptions fileSystemOptions;
+
+    /**
+     * How many fileObjects are handed out
+     */
+    private long useCount;
+
+
+    private FileSystemKey cacheKey;
+
+    /**
+     * open streams counter for this filesystem
+     */
+    private int openStreams;
+
+    protected AbstractFileSystem(final FileName rootName,
+                                 final FileObject parentLayer,
+                                 final FileSystemOptions fileSystemOptions)
+    {
+        // this.parentLayer = parentLayer;
+        this.parentLayer = parentLayer;
+        this.rootName = rootName;
+        this.fileSystemOptions = fileSystemOptions;
+        FileSystemConfigBuilder builder = DefaultFileSystemConfigBuilder.getInstance();
+        String uri = builder.getRootURI(fileSystemOptions);
+        if (uri == null)
+        {
+            uri = rootName.getURI();
+        }
+        this.rootURI = uri;
+    }
+
+    /**
+     * Initialises this component.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void init() throws FileSystemException
+    {
+        addCapabilities(caps);
+    }
+
+    /**
+     * Closes this component.
+     */
+    public void close()
+    {
+        closeCommunicationLink();
+
+        parentLayer = null;
+    }
+
+    /**
+     * Close the underlaying link used to access the files.
+     */
+    public void closeCommunicationLink()
+    {
+        synchronized (this)
+        {
+            doCloseCommunicationLink();
+        }
+    }
+
+    /**
+     * Close the underlaying link used to access the files
+     */
+    protected void doCloseCommunicationLink()
+    {
+    }
+
+    /**
+     * Creates a file object.  This method is called only if the requested
+     * file is not cached.
+     */
+    protected abstract FileObject createFile(final FileName name)
+        throws Exception;
+
+    /**
+     * Adds the capabilities of this file system.
+     */
+    protected abstract void addCapabilities(Collection caps);
+
+    /**
+     * Returns the name of the root of this file system.
+     * @return the root FileName.
+     */
+    public FileName getRootName()
+    {
+        return rootName;
+    }
+
+    /**
+     * Returns the root URI specified for this file System.
+     * @return The root URI used in this file system.
+     */
+    public String getRootURI()
+    {
+        return rootURI;
+    }
+
+    /**
+     * Adds a file object to the cache.
+     */
+    protected void putFileToCache(final FileObject file)
+    {
+        getCache().putFile(file);
+        // files.put(file.getName(), file);
+    }
+
+    private FilesCache getCache()
+    {
+        FilesCache files;
+        //if (this.files == null)
+        //{
+            files = getContext().getFileSystemManager().getFilesCache();
+            if (files == null)
+            {
+                throw new RuntimeException(Messages.getString("vfs.provider/files-cache-missing.error"));
+            }
+        //}
+
+        return files;
+    }
+
+    /**
+     * Returns a cached file.
+     */
+    protected FileObject getFileFromCache(final FileName name)
+    {
+        return getCache().getFile(this, name);
+        // return (FileObject) files.get(name);
+    }
+
+    /**
+     * remove a cached file.
+     */
+    protected void removeFileFromCache(final FileName name)
+    {
+        getCache().removeFile(this, name);
+    }
+
+    /**
+     * Determines if this file system has a particular capability.
+     * @param capability the Capability to check for.
+     * @return true if the FileSystem has the Capability, false otherwise.
+     */
+    public boolean hasCapability(final Capability capability)
+    {
+        return caps.contains(capability);
+    }
+
+    /**
+     * Retrieves the attribute with the specified name. The default
+     * implementation simply throws an exception.
+     * @param attrName The name of the attribute.
+     * @return the Object associated with the attribute or null if no object is.
+     * @throws FileSystemException if an error occurs.
+     */
+    public Object getAttribute(final String attrName) throws FileSystemException
+    {
+        throw new FileSystemException("vfs.provider/get-attribute-not-supported.error");
+    }
+
+    /**
+     * Sets the attribute with the specified name. The default
+     * implementation simply throws an exception.
+     * @param attrName the attribute name.
+     * @param value The object to associate with the attribute.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void setAttribute(final String attrName, final Object value)
+        throws FileSystemException
+    {
+        throw new FileSystemException("vfs.provider/set-attribute-not-supported.error");
+    }
+
+    /**
+     * Returns the parent layer if this is a layered file system.
+     * @return The FileObject for the parent layer.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject getParentLayer() throws FileSystemException
+    {
+        return parentLayer;
+    }
+
+    /**
+     * Returns the root file of this file system.
+     * @return The root FileObject of the FileSystem
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject getRoot() throws FileSystemException
+    {
+        return resolveFile(rootName);
+        /*
+        if (root == null)
+        {
+            root = resolveFile(rootName);
+        }
+        return root;
+        */
+    }
+
+    /**
+     * Finds a file in this file system.
+     * @param nameStr The name of the file to resolve.
+     * @return The located FileObject or null if none could be located.
+     * @throws FileSystemException if an error occurs.
+     */
+    public FileObject resolveFile(final String nameStr) throws FileSystemException
+    {
+        // Resolve the name, and create the file
+        final FileName name = getFileSystemManager().resolveName(rootName, nameStr);
+        return resolveFile(name);
+    }
+
+    /**
+     * Finds a file in this file system.
+     * @param name The name of the file to locate.
+     * @return The located FileObject or null if none could be located.
+     * @throws FileSystemException if an error occurs.
+     */
+    public synchronized FileObject resolveFile(final FileName name) throws FileSystemException
+    {
+        return resolveFile(name, true);
+    }
+
+    private synchronized FileObject resolveFile(final FileName name, final boolean useCache) throws FileSystemException
+    {
+        if (!rootName.getRootURI().equals(name.getRootURI()))
+        {
+            throw new FileSystemException("vfs.provider/mismatched-fs-for-name.error",
+                new Object[]{
+                    name, rootName, name.getRootURI()});
+        }
+
+        // imario@apache.org ==> use getFileFromCache
+        FileObject file;
+        if (useCache)
+        {
+            file = getFileFromCache(name);
+        }
+        else
+        {
+            file = null;
+        }
+        // FileObject file = (FileObject) files.get(name);
+        if (file == null)
+        {
+            try
+            {
+                synchronized (this)
+                {
+                    file = createFile(name);
+                }
+            }
+            catch (Exception e)
+            {
+                throw new FileSystemException("vfs.provider/resolve-file.error", name, e);
+            }
+
+            file = decorateFileObject(file);
+
+            // imario@apache.org ==> use putFileToCache
+            if (useCache)
+            {
+                putFileToCache(file);
+            }
+            // files.put(name, file);
+        }
+
+        /**
+         * resync the file information if requested
+         */
+        if (getFileSystemManager().getCacheStrategy().equals(CacheStrategy.ON_RESOLVE))
+        {
+            file.refresh();
+        }
+        return file;
+    }
+
+    protected FileObject decorateFileObject(FileObject file)  throws FileSystemException
+    {
+        if (getFileSystemManager().getCacheStrategy().equals(CacheStrategy.ON_CALL))
+        {
+            file = new OnCallRefreshFileObject(file);
+        }
+
+        if (getFileSystemManager().getFileObjectDecoratorConst() != null)
+        {
+            try
+            {
+                file = (FileObject) getFileSystemManager().getFileObjectDecoratorConst().
+                        newInstance(new Object[]{file});
+            }
+            catch (InstantiationException e)
+            {
+                throw new FileSystemException("vfs.impl/invalid-decorator.error",
+                        getFileSystemManager().getFileObjectDecorator().getName(), e);
+            }
+            catch (IllegalAccessException e)
+            {
+                throw new FileSystemException("vfs.impl/invalid-decorator.error",
+                        getFileSystemManager().getFileObjectDecorator().getName(), e);
+            }
+            catch (InvocationTargetException e)
+            {
+                throw new FileSystemException("vfs.impl/invalid-decorator.error",
+                        getFileSystemManager().getFileObjectDecorator().getName(), e);
+            }
+        }
+
+        return file;
+    }
+
+    /**
+     * Creates a temporary local copy of a file and its descendents.
+     * @param file The FileObject to replicate.
+     * @param selector The FileSelector.
+     * @return The replicated File.
+     * @throws FileSystemException if an error occurs.
+     */
+    public File replicateFile(final FileObject file,
+                              final FileSelector selector)
+        throws FileSystemException
+    {
+        if (!file.exists())
+        {
+            throw new FileSystemException("vfs.provider/replicate-missing-file.error", file.getName());
+        }
+
+        try
+        {
+            return doReplicateFile(file, selector);
+        }
+        catch (final Exception e)
+        {
+            throw new FileSystemException("vfs.provider/replicate-file.error", file.getName(), e);
+        }
+    }
+
+    /**
+     * Return the FileSystemOptions used to instantiate this filesystem.
+     * @return the FileSystemOptions.
+     */
+    public FileSystemOptions getFileSystemOptions()
+    {
+        return fileSystemOptions;
+    }
+
+    /**
+     * Return the FileSystemManager used to instantiate this filesystem.
+     * @return the FileSystemManager.
+     */
+    public FileSystemManager getFileSystemManager()
+    {
+        return getContext().getFileSystemManager();
+        // return manager;
+    }
+
+    /**
+     * Returns the accuracy of the last modification time.
+     *
+     * @return ms 0 perfectly accurate, >0 might be off by this value e.g. sftp 1000ms
+     */
+    public double getLastModTimeAccuracy()
+    {
+        return 0;
+    }
+
+    /**
+     * Creates a temporary local copy of a file and its descendents.
+     */
+    protected File doReplicateFile(final FileObject file,
+                                   final FileSelector selector)
+        throws Exception
+    {
+        return getContext().getReplicator().replicateFile(file, selector);
+    }
+
+    /**
+     * Adds a junction to this file system.
+     * @param junctionPoint The junction point.
+     * @param targetFile The target to add.
+     * @throws FileSystemException if an error occurs.
+     */
+    public void addJunction(final String junctionPoint,
+                            final FileObject targetFile)
+        throws FileSystemException
+    {
+        throw new FileSystemException("vfs.provider/junctions-not-supported.error", rootName);
+    }
+
+    /**
+     * Removes a junction from this file system.
+     * @param junctionPoint The junction point.
+     * @throws FileSystemException if an error occurs
+     */
+    public void removeJunction(final String junctionPoint) throws FileSystemException
+    {
+        throw new FileSystemException("vfs.provider/junctions-not-supported.error", rootName);
+    }
+
+    /**
+     * Adds a listener on a file in this file system.
+     * @param file The FileObject to be monitored.
+     * @param listener The FileListener
+     */
+    public void addListener(final FileObject file,
+                            final FileListener listener)
+    {
+        synchronized (listenerMap)
+        {
+            ArrayList listeners = (ArrayList) listenerMap.get(file.getName());
+            if (listeners == null)
+            {
+                listeners = new ArrayList();
+                listenerMap.put(file.getName(), listeners);
+            }
+            listeners.add(listener);
+        }
+    }
+
+    /**
+     * Removes a listener from a file in this file system.
+     * @param file The FileObject to be monitored.
+     * @param listener The FileListener
+     */
+    public void removeListener(final FileObject file,
+                               final FileListener listener)
+    {
+        synchronized (listenerMap)
+        {
+            final ArrayList listeners = (ArrayList) listenerMap.get(file.getName());
+            if (listeners != null)
+            {
+                listeners.remove(listener);
+                if (listeners.isEmpty())
+                {
+                    listenerMap.remove(file.getName());
+                }
+            }
+        }
+    }
+
+    /**
+     * Fires a file create event.
+     * @param file The FileObject that was created.
+     */
+    public void fireFileCreated(final FileObject file)
+    {
+        fireEvent(new CreateEvent(file));
+    }
+
+    /**
+     * Fires a file delete event.
+     * @param file The FileObject that was deleted.
+     */
+    public void fireFileDeleted(final FileObject file)
+    {
+        fireEvent(new DeleteEvent(file));
+    }
+
+    /**
+     * Fires a file changed event. <br />
+     * This will only happen if you monitor the file using {@link org.apache.commons.vfs.FileMonitor}.
+     * @param file The FileObject that changed.
+     */
+    public void fireFileChanged(final FileObject file)
+    {
+        fireEvent(new ChangedEvent(file));
+    }
+
+    /**
+     * returns true if no file is using this filesystem.
+     * @return true of no file is using this FileSystem.
+     */
+    public boolean isReleaseable()
+    {
+        return useCount < 1;
+    }
+
+    void freeResources()
+    {
+    }
+
+    /**
+     * Fires an event.
+     */
+    private void fireEvent(final AbstractFileChangeEvent event)
+    {
+        FileListener[] fileListeners = null;
+        final FileObject file = event.getFile();
+
+        synchronized (listenerMap)
+        {
+            final ArrayList listeners = (ArrayList) listenerMap.get(file.getName());
+            if (listeners != null)
+            {
+                fileListeners = (FileListener[]) listeners.toArray(new FileListener[listeners.size()]);
+            }
+        }
+
+        if (fileListeners != null)
+        {
+            for (int i = 0; i < fileListeners.length; i++)
+            {
+                final FileListener fileListener = fileListeners[i];
+                try
+                {
+                    event.notify(fileListener);
+                }
+                catch (final Exception e)
+                {
+                    final String message = Messages.getString("vfs.provider/notify-listener.warn", file);
+                    // getLogger().warn(message, e);
+                    VfsLog.warn(getLogger(), LOG, message, e);
+                }
+            }
+        }
+    }
+
+    /*
+    void fileDetached(FileObject fileObject)
+    {
+        useCount--;
+    }
+
+    void fileAttached(FileObject fileObject)
+    {
+        useCount++;
+
+    }
+    */
+
+    void fileObjectHanded(FileObject fileObject)
+    {
+        useCount++;
+    }
+
+    void fileObjectDestroyed(FileObject fileObject)
+    {
+        useCount--;
+    }
+
+    void setCacheKey(FileSystemKey cacheKey)
+    {
+        this.cacheKey = cacheKey;
+    }
+
+    FileSystemKey getCacheKey()
+    {
+        return this.cacheKey;
+    }
+
+    void streamOpened()
+    {
+        synchronized (this)
+        {
+            openStreams++;
+        }
+    }
+
+    void streamClosed()
+    {
+        synchronized (this)
+        {
+            if (openStreams > 0)
+            {
+                openStreams--;
+                if (openStreams < 1)
+                {
+                    notifyAllStreamsClosed();
+                }
+            }
+        }
+    }
+
+    /**
+     * will be called after all file-objects closed their streams.
+     */
+    protected void notifyAllStreamsClosed()
+    {
+    }
+
+    /**
+     * check if this filesystem has open streams.
+     * @return true if the FileSystem has open streams.
+     */
+    public boolean isOpen()
+    {
+        synchronized (this)
+        {
+            return openStreams > 0;
+        }
+    }
+}