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