Author: imario Date: Mon Mar 19 13:54:27 2007 New Revision: 520100 URL: http://svn.apache.org/viewvc?view=rev&rev=520100 Log: VFS-114: avoid successive children refresh
Modified: jakarta/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java Modified: jakarta/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java?view=diff&rev=520100&r1=520099&r2=520100 ============================================================================== --- jakarta/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java (original) +++ jakarta/commons/proper/vfs/trunk/core/src/main/java/org/apache/commons/vfs/provider/ftp/FtpFileObject.java Mon Mar 19 13:54:27 2007 @@ -48,150 +48,154 @@ * @version $Revision$ $Date$ */ public class FtpFileObject - extends AbstractFileObject + extends AbstractFileObject { - private Log log = LogFactory.getLog(FtpFileObject.class); + private Log log = LogFactory.getLog(FtpFileObject.class); - private static final Map EMPTY_FTP_FILE_MAP = Collections.unmodifiableMap(new TreeMap()); + private static final Map EMPTY_FTP_FILE_MAP = Collections.unmodifiableMap(new TreeMap()); - private final FtpFileSystem ftpFs; - private final String relPath; + private final FtpFileSystem ftpFs; + private final String relPath; - // Cached info - private FTPFile fileInfo; - private Map children; - private FileObject linkDestination; + // Cached info + private FTPFile fileInfo; + private Map children; + private FileObject linkDestination; - private boolean inRefresh=false; + private boolean inRefresh = false; protected FtpFileObject(final FileName name, - final FtpFileSystem fileSystem, - final FileName rootName) - throws FileSystemException - { - super(name, fileSystem); - ftpFs = fileSystem; - String relPath = UriParser.decode(rootName.getRelativeName(name)); - if (".".equals(relPath)) - { - // do not use the "." as path against the ftp-server - // e.g. the uu.net ftp-server do a recursive listing then - // this.relPath = UriParser.decode(rootName.getPath()); - // this.relPath = "."; - this.relPath = null; - } - else - { - this.relPath = relPath; - } - } - - /** - * Called by child file objects, to locate their ftp file info. - * - * @param name the filename in its native form ie. without uri stuff (%nn) - * @param flush recreate children cache - */ - private FTPFile getChildFile(final String name, final boolean flush) throws IOException - { - if (flush) - { - children = null; - } - - // List the children of this file - doGetChildren(); - - // Look for the requested child - FTPFile ftpFile = (FTPFile) children.get(name); - return ftpFile; - } - - /** - * Fetches the children of this file, if not already cached. - */ - private void doGetChildren() throws IOException - { - if (children != null) - { - return; - } - - final FtpClient client = ftpFs.getClient(); - try - { - final FTPFile[] tmpChildren = client.listFiles(relPath); - if (tmpChildren == null || tmpChildren.length == 0) - { - children = EMPTY_FTP_FILE_MAP; - } - else - { - children = new TreeMap(); - - // Remove '.' and '..' elements - for (int i = 0; i < tmpChildren.length; i++) - { - final FTPFile child = tmpChildren[i]; - if (child == null) - { - if (log.isDebugEnabled()) - { - log.debug(Messages.getString("vfs.provider.ftp/invalid-directory-entry.debug", - new Object[] - { - new Integer(i), relPath - })); - } - continue; - } - if (!".".equals(child.getName()) - && !"..".equals(child.getName())) - { - children.put(child.getName(), child); - } - } - } - } - finally - { - ftpFs.putClient(client); - } - } - - /** - * Attaches this file object to its file resource. - */ - protected void doAttach() - throws IOException - { - // Get the parent folder to find the info for this file - getInfo(false); - } - - /** - * Fetches the info for this file. - */ - private void getInfo(boolean flush) throws IOException - { - final FtpFileObject parent = (FtpFileObject) FileObjectUtils.getAbstractFileObject(getParent()); - FTPFile newFileInfo; - if (parent != null) - { - newFileInfo = parent.getChildFile(UriParser.decode(getName().getBaseName()), flush); - } - else - { - // Assume the root is a directory and exists - newFileInfo = new FTPFile(); - newFileInfo.setType(FTPFile.DIRECTORY_TYPE); - } - - this.fileInfo = newFileInfo; - } + final FtpFileSystem fileSystem, + final FileName rootName) + throws FileSystemException + { + super(name, fileSystem); + ftpFs = fileSystem; + String relPath = UriParser.decode(rootName.getRelativeName(name)); + if (".".equals(relPath)) + { + // do not use the "." as path against the ftp-server + // e.g. the uu.net ftp-server do a recursive listing then + // this.relPath = UriParser.decode(rootName.getPath()); + // this.relPath = "."; + this.relPath = null; + } + else + { + this.relPath = relPath; + } + } /** + * Called by child file objects, to locate their ftp file info. * + * @param name the filename in its native form ie. without uri stuff (%nn) + * @param flush recreate children cache + */ + private FTPFile getChildFile(final String name, final boolean flush) throws IOException + { + /* If we should flush cached children, clear our children map unless + * we're in the middle of a refresh in which case we've just recently + * refreshed our children. No need to do it again when our children are + * refresh()ed, calling getChildFile() for themselves from within + * getInfo(). See getChildren(). */ + if (flush && !inRefresh) + { + children = null; + } + + // List the children of this file + doGetChildren(); + + // Look for the requested child + FTPFile ftpFile = (FTPFile) children.get(name); + return ftpFile; + } + + /** + * Fetches the children of this file, if not already cached. + */ + private void doGetChildren() throws IOException + { + if (children != null) + { + return; + } + + final FtpClient client = ftpFs.getClient(); + try + { + final FTPFile[] tmpChildren = client.listFiles(relPath); + if (tmpChildren == null || tmpChildren.length == 0) + { + children = EMPTY_FTP_FILE_MAP; + } + else + { + children = new TreeMap(); + + // Remove '.' and '..' elements + for (int i = 0; i < tmpChildren.length; i++) + { + final FTPFile child = tmpChildren[i]; + if (child == null) + { + if (log.isDebugEnabled()) + { + log.debug(Messages.getString("vfs.provider.ftp/invalid-directory-entry.debug", + new Object[] + { + new Integer(i), relPath + })); + } + continue; + } + if (!".".equals(child.getName()) + && !"..".equals(child.getName())) + { + children.put(child.getName(), child); + } + } + } + } + finally + { + ftpFs.putClient(client); + } + } + + /** + * Attaches this file object to its file resource. + */ + protected void doAttach() + throws IOException + { + // Get the parent folder to find the info for this file + getInfo(false); + } + + /** + * Fetches the info for this file. + */ + private void getInfo(boolean flush) throws IOException + { + final FtpFileObject parent = (FtpFileObject) FileObjectUtils.getAbstractFileObject(getParent()); + FTPFile newFileInfo; + if (parent != null) + { + newFileInfo = parent.getChildFile(UriParser.decode(getName().getBaseName()), flush); + } + else + { + // Assume the root is a directory and exists + newFileInfo = new FTPFile(); + newFileInfo.setType(FTPFile.DIRECTORY_TYPE); + } + + this.fileInfo = newFileInfo; + } + + /** * @throws FileSystemException */ public void refresh() throws FileSystemException @@ -220,391 +224,419 @@ } /** - * Detaches this file object from its file resource. - */ - protected void doDetach() - { - this.fileInfo = null; - children = null; - } - - /** - * Called when the children of this file change. - */ - protected void onChildrenChanged(FileName child, FileType newType) - { - if (children != null && newType.equals(FileType.IMAGINARY)) - { - try - { - children.remove(UriParser.decode(child.getBaseName())); - } - catch (FileSystemException e) - { - throw new RuntimeException(e.getMessage()); - } - } - else - { - // if child was added we have to rescan the children - // TODO - get rid of this - children = null; - } - } - - /** - * Called when the type or content of this file changes. - */ - protected void onChange() throws IOException - { - children = null; - - if (getType().equals(FileType.IMAGINARY)) - { - // file is deleted, avoid server lookup - this.fileInfo = null; - return; - } - - getInfo(true); - } - - /** - * Determines the type of the file, returns null if the file does not - * exist. - */ - protected FileType doGetType() - throws Exception - { - if (this.fileInfo == null) - { - return FileType.IMAGINARY; - } - else if (this.fileInfo.isDirectory()) - { - return FileType.FOLDER; - } - else if (this.fileInfo.isFile()) - { - return FileType.FILE; - } - else if (this.fileInfo.isSymbolicLink()) - { - return getLinkDestination().getType(); - } - - throw new FileSystemException("vfs.provider.ftp/get-type.error", getName()); - } - - private FileObject getLinkDestination() throws FileSystemException - { - if (linkDestination == null) - { - final String path = this.fileInfo.getLink(); - FileName relativeTo = getName().getParent(); - if (relativeTo == null) - { - relativeTo = getName(); - } - FileName linkDestinationName = getFileSystem().getFileSystemManager().resolveName(relativeTo, path); - linkDestination = getFileSystem().resolveFile(linkDestinationName); - } - - return linkDestination; - } - - protected FileObject[] doListChildrenResolved() throws Exception - { - if (this.fileInfo.isSymbolicLink()) - { - return getLinkDestination().getChildren(); - } - - return null; - } - - /** - * Lists the children of the file. - */ - protected String[] doListChildren() - throws Exception - { - // List the children of this file - doGetChildren(); - - // TODO - get rid of this children stuff - final String[] childNames = new String[children.size()]; - int childNum = -1; - Iterator iterChildren = children.values().iterator(); - while (iterChildren.hasNext()) - { - childNum++; - final FTPFile child = (FTPFile) iterChildren.next(); - childNames[childNum] = child.getName(); - } - - return UriParser.encode(childNames); - } - - /** - * Deletes the file. - */ - protected void doDelete() throws Exception - { - final boolean ok; - final FtpClient ftpClient = ftpFs.getClient(); - try - { - if (this.fileInfo.isDirectory()) - { - ok = ftpClient.removeDirectory(relPath); - } - else - { - ok = ftpClient.deleteFile(relPath); - } - } - finally - { - ftpFs.putClient(ftpClient); - } - - if (!ok) - { - throw new FileSystemException("vfs.provider.ftp/delete-file.error", getName()); - } - this.fileInfo = null; - children = EMPTY_FTP_FILE_MAP; - } - - /** - * Renames the file - */ - protected void doRename(FileObject newfile) throws Exception - { - final boolean ok; - final FtpClient ftpClient = ftpFs.getClient(); - try - { - String oldName = getName().getPath(); - String newName = newfile.getName().getPath(); - ok = ftpClient.rename(oldName, newName); - } - finally - { - ftpFs.putClient(ftpClient); - } - - if (!ok) - { - throw new FileSystemException("vfs.provider.ftp/rename-file.error", new Object[]{getName().toString(), newfile}); - } - this.fileInfo = null; - children = EMPTY_FTP_FILE_MAP; - } - - /** - * Creates this file as a folder. - */ - protected void doCreateFolder() - throws Exception - { - final boolean ok; - final FtpClient client = ftpFs.getClient(); - try - { - ok = client.makeDirectory(relPath); - } - finally - { - ftpFs.putClient(client); - } - - if (!ok) - { - throw new FileSystemException("vfs.provider.ftp/create-folder.error", getName()); - } - } - - /** - * Returns the size of the file content (in bytes). - */ - protected long doGetContentSize() throws Exception - { - if (this.fileInfo.isSymbolicLink()) - { - return getLinkDestination().getContent().getSize(); - } - else - { - return this.fileInfo.getSize(); - } - } - - /** - * get the last modified time on an ftp file - * - * @see org.apache.commons.vfs.provider.AbstractFileObject#doGetLastModifiedTime() - */ - protected long doGetLastModifiedTime() throws Exception - { - if (this.fileInfo.isSymbolicLink()) - { - return getLinkDestination().getContent().getLastModifiedTime(); - } - else - { - Calendar timestamp = this.fileInfo.getTimestamp(); - if (timestamp == null) - { - return 0L; - } - else - { - return (timestamp.getTime().getTime()); - } - } - } - - /** - * Creates an input stream to read the file content from. - */ - protected InputStream doGetInputStream() throws Exception - { - final FtpClient client = ftpFs.getClient(); - final InputStream instr = client.retrieveFileStream(relPath); - return new FtpInputStream(client, instr); - } - - protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception - { - return new FtpRandomAccessContent(this, mode); - } - - /** - * Creates an output stream to write the file content to. - */ - protected OutputStream doGetOutputStream(boolean bAppend) - throws Exception - { - final FtpClient client = ftpFs.getClient(); - OutputStream out = null; - if (bAppend) - { - out = client.appendFileStream(relPath); - } - else - { - out = client.storeFileStream(relPath); - } - - if (out == null) - { - throw new FileSystemException("vfs.provider.ftp/output-error.debug", new Object[] - { - this.getName(), - client.getReplyString() - }); - } - - return new FtpOutputStream(client, out); - } - - String getRelPath() - { - return relPath; - } - - FtpInputStream getInputStream(long filePointer) throws IOException - { - final FtpClient client = ftpFs.getClient(); - final InputStream instr = client.retrieveFileStream(relPath, filePointer); - if (instr == null) - { - throw new FileSystemException("vfs.provider.ftp/input-error.debug", new Object[] - { - this.getName(), - client.getReplyString() - }); - } - return new FtpInputStream(client, instr); - } - - /** - * An InputStream that monitors for end-of-file. - */ - class FtpInputStream - extends MonitorInputStream - { - private final FtpClient client; - - public FtpInputStream(final FtpClient client, final InputStream in) - { - super(in); - this.client = client; - } - - void abort() throws IOException - { - client.abort(); - close(); - } - - /** - * Called after the stream has been closed. - */ - protected void onClose() throws IOException - { - final boolean ok; - try - { - ok = client.completePendingCommand(); - } - finally - { - ftpFs.putClient(client); - } - - if (!ok) - { - throw new FileSystemException("vfs.provider.ftp/finish-get.error", getName()); - } - } - } - - /** - * An OutputStream that monitors for end-of-file. - */ - private class FtpOutputStream - extends MonitorOutputStream - { - private final FtpClient client; - - public FtpOutputStream(final FtpClient client, final OutputStream outstr) - { - super(outstr); - this.client = client; - } - - /** - * Called after this stream is closed. - */ - protected void onClose() throws IOException - { - final boolean ok; - try - { - ok = client.completePendingCommand(); - } - finally - { - ftpFs.putClient(client); - } - - if (!ok) - { - throw new FileSystemException("vfs.provider.ftp/finish-put.error", getName()); - } - } - } + * Detaches this file object from its file resource. + */ + protected void doDetach() + { + this.fileInfo = null; + children = null; + } + + /** + * Called when the children of this file change. + */ + protected void onChildrenChanged(FileName child, FileType newType) + { + if (children != null && newType.equals(FileType.IMAGINARY)) + { + try + { + children.remove(UriParser.decode(child.getBaseName())); + } + catch (FileSystemException e) + { + throw new RuntimeException(e.getMessage()); + } + } + else + { + // if child was added we have to rescan the children + // TODO - get rid of this + children = null; + } + } + + /** + * Called when the type or content of this file changes. + */ + protected void onChange() throws IOException + { + children = null; + + if (getType().equals(FileType.IMAGINARY)) + { + // file is deleted, avoid server lookup + this.fileInfo = null; + return; + } + + getInfo(true); + } + + /** + * Determines the type of the file, returns null if the file does not + * exist. + */ + protected FileType doGetType() + throws Exception + { + if (this.fileInfo == null) + { + return FileType.IMAGINARY; + } + else if (this.fileInfo.isDirectory()) + { + return FileType.FOLDER; + } + else if (this.fileInfo.isFile()) + { + return FileType.FILE; + } + else if (this.fileInfo.isSymbolicLink()) + { + return getLinkDestination().getType(); + } + + throw new FileSystemException("vfs.provider.ftp/get-type.error", getName()); + } + + private FileObject getLinkDestination() throws FileSystemException + { + if (linkDestination == null) + { + final String path = this.fileInfo.getLink(); + FileName relativeTo = getName().getParent(); + if (relativeTo == null) + { + relativeTo = getName(); + } + FileName linkDestinationName = getFileSystem().getFileSystemManager().resolveName(relativeTo, path); + linkDestination = getFileSystem().resolveFile(linkDestinationName); + } + + return linkDestination; + } + + protected FileObject[] doListChildrenResolved() throws Exception + { + if (this.fileInfo.isSymbolicLink()) + { + return getLinkDestination().getChildren(); + } + + return null; + } + + /** + * Returns the file's list of children. + * + * @return The list of children + * @throws FileSystemException If there was a problem listing children + * @see AbstractFileObject#getChildren() + * @since 1.0 + */ + public FileObject[] getChildren() throws FileSystemException + { + try + { + /* Wrap our parent implementation, noting that we're refreshing so + * that we don't refresh() ourselves and each of our parents for + * each children. Note that refresh() will list children. Meaning, + * if if this file has C children, P parents, there will be (C * P) + * listings made with (C * (P + 1)) refreshes, when there should + * really only be 1 listing and C refreshes. */ + + this.inRefresh = true; + return super.getChildren(); + } + finally + { + this.inRefresh = false; + } + } + + /** + * Lists the children of the file. + */ + protected String[] doListChildren() + throws Exception + { + // List the children of this file + doGetChildren(); + + // TODO - get rid of this children stuff + final String[] childNames = new String[children.size()]; + int childNum = -1; + Iterator iterChildren = children.values().iterator(); + while (iterChildren.hasNext()) + { + childNum++; + final FTPFile child = (FTPFile) iterChildren.next(); + childNames[childNum] = child.getName(); + } + + return UriParser.encode(childNames); + } + + /** + * Deletes the file. + */ + protected void doDelete() throws Exception + { + final boolean ok; + final FtpClient ftpClient = ftpFs.getClient(); + try + { + if (this.fileInfo.isDirectory()) + { + ok = ftpClient.removeDirectory(relPath); + } + else + { + ok = ftpClient.deleteFile(relPath); + } + } + finally + { + ftpFs.putClient(ftpClient); + } + + if (!ok) + { + throw new FileSystemException("vfs.provider.ftp/delete-file.error", getName()); + } + this.fileInfo = null; + children = EMPTY_FTP_FILE_MAP; + } + + /** + * Renames the file + */ + protected void doRename(FileObject newfile) throws Exception + { + final boolean ok; + final FtpClient ftpClient = ftpFs.getClient(); + try + { + String oldName = getName().getPath(); + String newName = newfile.getName().getPath(); + ok = ftpClient.rename(oldName, newName); + } + finally + { + ftpFs.putClient(ftpClient); + } + + if (!ok) + { + throw new FileSystemException("vfs.provider.ftp/rename-file.error", new Object[]{getName().toString(), newfile}); + } + this.fileInfo = null; + children = EMPTY_FTP_FILE_MAP; + } + + /** + * Creates this file as a folder. + */ + protected void doCreateFolder() + throws Exception + { + final boolean ok; + final FtpClient client = ftpFs.getClient(); + try + { + ok = client.makeDirectory(relPath); + } + finally + { + ftpFs.putClient(client); + } + + if (!ok) + { + throw new FileSystemException("vfs.provider.ftp/create-folder.error", getName()); + } + } + + /** + * Returns the size of the file content (in bytes). + */ + protected long doGetContentSize() throws Exception + { + if (this.fileInfo.isSymbolicLink()) + { + return getLinkDestination().getContent().getSize(); + } + else + { + return this.fileInfo.getSize(); + } + } + + /** + * get the last modified time on an ftp file + * + * @see org.apache.commons.vfs.provider.AbstractFileObject#doGetLastModifiedTime() + */ + protected long doGetLastModifiedTime() throws Exception + { + if (this.fileInfo.isSymbolicLink()) + { + return getLinkDestination().getContent().getLastModifiedTime(); + } + else + { + Calendar timestamp = this.fileInfo.getTimestamp(); + if (timestamp == null) + { + return 0L; + } + else + { + return (timestamp.getTime().getTime()); + } + } + } + + /** + * Creates an input stream to read the file content from. + */ + protected InputStream doGetInputStream() throws Exception + { + final FtpClient client = ftpFs.getClient(); + final InputStream instr = client.retrieveFileStream(relPath); + return new FtpInputStream(client, instr); + } + + protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception + { + return new FtpRandomAccessContent(this, mode); + } + + /** + * Creates an output stream to write the file content to. + */ + protected OutputStream doGetOutputStream(boolean bAppend) + throws Exception + { + final FtpClient client = ftpFs.getClient(); + OutputStream out = null; + if (bAppend) + { + out = client.appendFileStream(relPath); + } + else + { + out = client.storeFileStream(relPath); + } + + if (out == null) + { + throw new FileSystemException("vfs.provider.ftp/output-error.debug", new Object[] + { + this.getName(), + client.getReplyString() + }); + } + + return new FtpOutputStream(client, out); + } + + String getRelPath() + { + return relPath; + } + + FtpInputStream getInputStream(long filePointer) throws IOException + { + final FtpClient client = ftpFs.getClient(); + final InputStream instr = client.retrieveFileStream(relPath, filePointer); + if (instr == null) + { + throw new FileSystemException("vfs.provider.ftp/input-error.debug", new Object[] + { + this.getName(), + client.getReplyString() + }); + } + return new FtpInputStream(client, instr); + } + + /** + * An InputStream that monitors for end-of-file. + */ + class FtpInputStream + extends MonitorInputStream + { + private final FtpClient client; + + public FtpInputStream(final FtpClient client, final InputStream in) + { + super(in); + this.client = client; + } + + void abort() throws IOException + { + client.abort(); + close(); + } + + /** + * Called after the stream has been closed. + */ + protected void onClose() throws IOException + { + final boolean ok; + try + { + ok = client.completePendingCommand(); + } + finally + { + ftpFs.putClient(client); + } + + if (!ok) + { + throw new FileSystemException("vfs.provider.ftp/finish-get.error", getName()); + } + } + } + + /** + * An OutputStream that monitors for end-of-file. + */ + private class FtpOutputStream + extends MonitorOutputStream + { + private final FtpClient client; + + public FtpOutputStream(final FtpClient client, final OutputStream outstr) + { + super(outstr); + this.client = client; + } + + /** + * Called after this stream is closed. + */ + protected void onClose() throws IOException + { + final boolean ok; + try + { + ok = client.completePendingCommand(); + } + finally + { + ftpFs.putClient(client); + } + + if (!ok) + { + throw new FileSystemException("vfs.provider.ftp/finish-put.error", getName()); + } + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]