Version 2

    Overview

    By default, JBoss VFS support 2 kinks of file system:

    • Real file system
    • Zip achieves(zip, war, ear, jar, etc)

    to support ftp file system, we need extend JBoss VFS add a ftp file system implementation, then mount it to a specific mount point of VFS, this article will demonstrate this.

    More details about JBoss VFS refer to VFS User Guide and Source Code.

    FtpFileSystem Implementation

    A FTPClient from commons-net is used to communicate with FTP Server, the  FtpFileSystem implementation as below:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.security.CodeSigner;
    import java.util.Arrays;
    import java.util.List;
    import java.util.concurrent.Executors;
    
    import org.apache.commons.net.ftp.FTPClient;
    import org.jboss.vfs.TempFileProvider;
    import org.jboss.vfs.VFS;
    import org.jboss.vfs.VirtualFile;
    import org.jboss.vfs.spi.FileSystem;
    
    public class FtpFileSystem implements FileSystem {
        
        private FTPClient client;
        
        private boolean isMount = false;
        
        private VirtualFile tmpPoint;
        
        public FtpFileSystem(FTPClient ftpClient) {
            this.client = ftpClient;
        }
    
        @Override
        public File getFile(VirtualFile mountPoint, VirtualFile target) throws IOException {
            if(!isMount) {
                VirtualFile tmp = mountPoint.getChild("tmp");
                TempFileProvider provider = TempFileProvider.create("tmp", Executors.newScheduledThreadPool(2));
                VFS.mountTemp(tmp, provider);
                this.tmpPoint = tmp;
                isMount = true;
            }
            if(!tmpPoint.getChild(target.getName()).exists()) {
                download(tmpPoint.getChild(target.getName()));
            }
            return tmpPoint.getChild(target.getName()).getPhysicalFile();
        }
    
        private void download(VirtualFile file) throws IOException {
            
            file.getPhysicalFile().createNewFile();
            OutputStream out = new FileOutputStream(file.getPhysicalFile());
            boolean completed = this.client.retrieveFile(file.getName(), out);
            if(!completed) {
                throw new IOException("Faile to download " + file); 
            }
        }
    
        @Override
        public InputStream openInputStream(VirtualFile mountPoint, VirtualFile target) throws IOException {
            return new FileInputStream(getFile(mountPoint, target));
        }
    
        @Override
        public boolean isReadOnly() {
            return false;
        }
    
        @Override
        public boolean delete(VirtualFile mountPoint, VirtualFile target) {
            if(this.tmpPoint != null && this.tmpPoint.getChild(target.getName()).exists()) {
                this.tmpPoint.getChild(target.getName()).delete();
            }
            try {
                return this.client.deleteFile(target.getName());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public long getSize(VirtualFile mountPoint, VirtualFile target) {
            try {
                return getFile(mountPoint, target).length();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public long getLastModified(VirtualFile mountPoint, VirtualFile target) {
            try {
                return getFile(mountPoint, target).lastModified();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public boolean exists(VirtualFile mountPoint, VirtualFile target) {
            String name = target.getName();
            if(this.tmpPoint != null && this.tmpPoint.getChild(name).exists()) {
                return true;
            }
            try {
                String[] names = this.client.listNames();
                boolean exists = false;
                if (names != null && names.length > 0){
                    for(String n : names) {
                        if(n.equals(name)) {
                            exists = true;
                            break;
                        }
                    }
                }
                return exists;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
        }
    
        @Override
        public boolean isFile(VirtualFile mountPoint, VirtualFile target) {
            try {
                return getFile(mountPoint, target).isFile();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public boolean isDirectory(VirtualFile mountPoint, VirtualFile target) {
            try {
                if(this.client.cwd(target.getPathName()) == 250) {
                    return true;
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            } 
            return false;
        }
    
        @Override
        public List<String> getDirectoryEntries(VirtualFile mountPoint, VirtualFile target) {
            try {
                return Arrays.asList(this.client.listNames(target.getPathName()));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @Override
        public CodeSigner[] getCodeSigners(VirtualFile mountPoint, VirtualFile target) {
            return null;
        }
    
        @Override
        public void close() throws IOException {
            this.client.disconnect();
        }
    
        @Override
        public File getMountSource() {
            return null;
        }
    
        @Override
        public URI getRootURI() throws URISyntaxException {
            try {
                return new URI("ftp", this.client.printWorkingDirectory() + "!/", null);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
    }
    

    Mount FtpFileSystem

    Assuming remote ftp file system path is '/home/kylin/vsftpd', then mount FtpFileSystem as below:

    FtpFileSystem ftpFileSystem = new FtpFileSystem(ftpClient);
    VirtualFile mountPoint = VFS.getChild("/home/kylin/vsftpd");
    VFS.mount(mountPoint, ftpFileSystem);
    

    Note that, once mount success, we can use the path '/home/kylin/vsftpd/<any path to file>' to navigate and get the files under ftp server.

    Example: get file from ftp server

    "marketdata-price.txt" are the file exist in ftp server, its absolute path is "/home/kylin/vsftpd/marketdata-price.txt",  use the VFS API to get this file as below:

    VirtualFile marketdata = VFS.getChild("/home/kylin/vsftpd/marketdata-price.txt");
    marketdata.isDirectory();
    marketdata.isFile();
    marketdata.getSize();
    marketdata.getLastModified();
    marketdata.openStream();
    

    Example: delete file from ftp server

    "marketdata-price.csv" are the file exist in ftp server, its absolute path is "/home/kylin/vsftpd/marketdata-price.csv",  use the VFS API to delete this file as below:

    VirtualFile marketdata = VFS.getChild("/home/kylin/vsftpd/marketdata-price.csv");
    marketdata.delete();