hd3gtv.storage.AbstractFileBridgeFtp.java Source code

Java tutorial

Introduction

Here is the source code for hd3gtv.storage.AbstractFileBridgeFtp.java

Source

/*
 * This file is part of hd3g.tv' Java Storage Abstraction
 * 
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * Copyright (C) hdsdi3g for hd3g.tv 2013
 * 
*/
package hd3gtv.storage;

import hd3gtv.log2.Log2;
import hd3gtv.log2.Log2Dump;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Calendar;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

public class AbstractFileBridgeFtp implements AbstractFile {

    private StorageConfigurator configurator;
    private FTPClient ftpclient;
    private FTPFile file;

    /**
     * Chroot path
     */
    private String path;

    /**
     * Root path, end with a "/"
     */
    private final String root_path;

    public AbstractFileBridgeFtp(StorageConfigurator configurator) throws IOException {
        this.configurator = configurator;
        ftpclient = new FTPClient();
        reconnect();

        if (configurator.path.equals("/") == false) {
            if (configurator.path.startsWith("/") == false) {
                configurator.path = "/" + configurator.path;
            }
            if (configurator.path.endsWith("/")) {
                configurator.path = configurator.path.substring(0, configurator.path.length() - 1);
            }

            root_path = configurator.path;
        } else {
            root_path = "/";
        }

        file = getRandomFtpFile(configurator.path);

        if (file == null) {
            throw new IOException("Can't found root path");
        }

        path = "/";
    }

    private void reconnect() throws IOException {
        try {
            ftpclient.disconnect();
        } catch (Exception e) {
        }

        ftpclient.connect(configurator.host, configurator.port);

        if (ftpclient.login(configurator.username, configurator.password) == false) {
            ftpclient.logout();
            throw new IOException("Can't login to server");
        }
        int reply = ftpclient.getReplyCode();
        if (FTPReply.isPositiveCompletion(reply) == false) {
            ftpclient.disconnect();
            throw new IOException("Can't login to server");
        }

        ftpclient.setFileType(FTP.BINARY_FILE_TYPE);

        if (configurator.passive) {
            ftpclient.enterLocalPassiveMode();
        } else {
            ftpclient.enterLocalActiveMode();
        }
    }

    private AbstractFileBridgeFtp(AbstractFileBridgeFtp referer, FTPFile file, String path) {
        this.configurator = referer.configurator;
        this.ftpclient = referer.ftpclient;
        this.file = file;
        this.path = path;

        if (file.isDirectory() & (path.endsWith("/") == false)) {
            this.path = path + "/";
        }

        root_path = referer.root_path;
    }

    public Log2Dump getLog2Dump() {
        Log2Dump dump = new Log2Dump();
        dump.add("root_path", root_path);
        dump.add("path", path);
        dump.add("ftpclient", ftpclient.getClass().getName());
        dump.add("configurator host", configurator.host);
        dump.add("configurator username", configurator.username);
        dump.add("configurator path", configurator.path);
        return dump;
    }

    public AbstractFile[] listFiles() {
        try {
            ftpclient.setListHiddenFiles(true);
            FTPFile[] list = ftpclient.listFiles(root_path + path);
            if (list == null) {
                return new AbstractFileBridgeFtp[1];
            }

            ArrayList<AbstractFileBridgeFtp> al_ablist = new ArrayList<AbstractFileBridgeFtp>(list.length);
            for (int pos = 0; pos < list.length; pos++) {
                if (list[pos].getName().equals(".")) {
                    continue;
                }
                if (list[pos].getName().equals("..")) {
                    continue;
                }
                al_ablist.add(new AbstractFileBridgeFtp(this, list[pos], path + list[pos].getName()));
            }

            if (al_ablist.size() == 0) {
                return new AbstractFileBridgeFtp[1];
            }

            return al_ablist.toArray(new AbstractFileBridgeFtp[al_ablist.size()]);
        } catch (IOException e) {
            Log2.log.error("Can't list files", e, this);
            return null;
        }
    }

    public boolean canRead() {
        return file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION);
    }

    public boolean canWrite() {
        if (configurator.readonly) {
            return false;
        }
        return file.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION);
    }

    public long lastModified() {
        return file.getTimestamp().getTimeInMillis();
    }

    public String getPath() {
        if (path.endsWith("/")) {
            return path.substring(0, path.length() - 1);
        } else {
            return path;
        }
    }

    public boolean isDirectory() {
        return file.isDirectory();
    }

    public boolean isFile() {
        return file.isFile();
    }

    public boolean isHidden() {
        return (file.getName().startsWith("."));
    }

    public String getName() {
        return file.getName();
    }

    public long length() {
        return file.getSize();
    }

    public void close() {
        try {
            if (ftpclient.isConnected()) {
                ftpclient.disconnect();
            }
        } catch (IOException e) {
            Log2.log.error("Can't disconnect to FTP server", e, this);
        }
    }

    /**
     * Fake FTPFile, only for respond to root path queries.
     */
    private class RootFTPFile extends FTPFile {

        private static final long serialVersionUID = 4499653893317959711L;

        public String getName() {
            return "";
        }

        public long getSize() {
            return 0;
        }

        public Calendar getTimestamp() {
            Calendar result = Calendar.getInstance();
            result.setTimeInMillis(0);
            return result;
        }

        public boolean hasPermission(int access, int permission) {
            return true;
        }

        public boolean isDirectory() {
            return true;
        }

        public boolean isFile() {
            return false;
        }

    }

    /**
     * Fake FTPFile, only for respond to new file queries.
     */
    private class EmptyFTPFile extends FTPFile {

        private static final long serialVersionUID = 3506850846197477990L;

        String name;

        public EmptyFTPFile(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public long getSize() {
            return -1;
        }

        public Calendar getTimestamp() {
            Calendar result = Calendar.getInstance();
            result.setTimeInMillis(0);
            return result;
        }

        public boolean hasPermission(int access, int permission) {
            return false;
        }

        public boolean isDirectory() {
            return false;
        }

        public boolean isFile() {
            return false;
        }

    }

    private class FTPClosableInputStream extends BufferedInputStream {
        public FTPClosableInputStream(InputStream in, int buffersize) {
            super(in, buffersize);
        }

        public void close() throws IOException {
            super.close();
            if ((ftpclient.isConnected() == false) | (ftpclient.completePendingCommand() == false)) {
                ftpclient.disconnect();
                reconnect();
            }
        }
    }

    public BufferedInputStream getInputStream(int buffersize) {
        try {
            ftpclient.setFileType(FTP.BINARY_FILE_TYPE);
            String fullpath = root_path + path;
            if (root_path.equals("/")) {
                fullpath = path;
            }

            InputStream is = ftpclient.retrieveFileStream(fullpath);
            if (is == null) {
                throw new NullPointerException("Retrieve File Stream is null");
            }
            return new FTPClosableInputStream(is, buffersize);
        } catch (IOException e) {
            Log2.log.error("Can't access to file", e, this);
        }
        return null;
    }

    private class FTPClosableOutputStream extends BufferedOutputStream {

        public FTPClosableOutputStream(OutputStream out, int buffer) {
            super(out, buffer);
        }

        public void close() throws IOException {
            super.close();
            if ((ftpclient.isConnected() == false) | (ftpclient.completePendingCommand() == false)) {
                ftpclient.disconnect();
                reconnect();
            }
        }

    }

    public BufferedOutputStream getOutputStream(int buffersize) {
        if (configurator.readonly) {
            return null;
        }
        try {
            ftpclient.setFileType(FTP.BINARY_FILE_TYPE);
            String fullpath = root_path + path;
            if (root_path.equals("/")) {
                fullpath = path;
            }

            OutputStream os = ftpclient.storeFileStream(fullpath);
            if (os == null) {
                throw new NullPointerException("Store File Stream is null");
            }
            return new FTPClosableOutputStream(os, buffersize);
        } catch (IOException e) {
            Log2.log.error("Can't access to file", e, this);
        }
        return null;
    }

    public AbstractFile moveTo(String newpath) {
        if (configurator.readonly) {
            return null;
        }
        if (newpath.startsWith("/") == false) {
            newpath = "/" + newpath;
        }

        try {
            String newpath_ = (root_path + newpath).replaceAll("//", "/");

            if (FTPReply.isPositiveIntermediate(ftpclient.rnfr(root_path + path))) {
                if (FTPReply.isPositiveCompletion(ftpclient.rnto(newpath_))) {
                    return new AbstractFileBridgeFtp(this, getRandomFtpFile(newpath_), newpath);
                } else {
                    throw new IOException("Can't rename, set to new name");
                }
            } else {
                throw new IOException("Can't rename, prepare name");
            }
        } catch (IOException e) {
            /** Maybe a security problem */
            Log2.log.error("Can't access to file", e, this);
            return null;
        }
    }

    public AbstractFile mkdir(String newpath) {
        if (configurator.readonly) {
            return null;
        }
        if (newpath.startsWith("/") == false) {
            newpath = "/" + newpath;
        }

        String fullpath = root_path + newpath;
        fullpath = fullpath.replaceAll("//", "/");

        try {

            if (ftpclient.makeDirectory(fullpath)) {
                return new AbstractFileBridgeFtp(this, getRandomFtpFile(fullpath), newpath);
            } else {
                return null;
            }
        } catch (IOException e) {
            /** Maybe a security problem */
            Log2.log.error("Can't access to file", e, this);
            return null;
        }
    }

    private FTPFile getRandomFtpFile(String newpath) {
        if (newpath.equals("/")) {
            return new RootFTPFile();
        } else {
            if (newpath.endsWith("/")) {
                newpath = newpath.substring(0, newpath.length() - 1);
            }
            if (newpath.startsWith("/") == false) {
                newpath = "/" + newpath;
            }
            /**
             * The function "ftpclient.mlistFile(pathname)" don't exist on all servers...
             * It do a list of the content of the parent path whose we want to set for path, and select our directory/file element
             * Stupid, but functionnal.
             */
            FTPFile[] list;
            try {
                ftpclient.setListHiddenFiles(true);
                list = ftpclient.listFiles(newpath.substring(0, newpath.lastIndexOf("/")));
            } catch (IOException e) {
                return null;
            }
            for (int pos = 0; pos < list.length; pos++) {
                if (list[pos].getName().equals(newpath.substring(newpath.lastIndexOf("/") + 1, newpath.length()))) {
                    return list[pos];
                }
            }
            /**
             * Ask file don't exist...
             */
            return new EmptyFTPFile(newpath.substring(newpath.lastIndexOf("/") + 1, newpath.length()));
        }
    }

    public AbstractFile getAbstractFile(String newpath) {
        if (newpath.startsWith("/") == false) {
            newpath = "/" + newpath;
        }

        return new AbstractFileBridgeFtp(this, getRandomFtpFile(root_path + newpath), newpath);
    }

    private boolean recursiveDirectoryDelete(String fullpath) {
        try {
            ftpclient.setListHiddenFiles(true);

            FTPFile[] list = ftpclient.listFiles(fullpath);
            String subfilename;
            for (int pos = 0; pos < list.length; pos++) {
                subfilename = (fullpath + "/" + list[pos].getName()).replaceAll("//", "/");

                if (list[pos].isDirectory()) {
                    if (recursiveDirectoryDelete(subfilename) == false) {
                        return false;
                    }
                } else {
                    if (ftpclient.deleteFile(subfilename) == false) {
                        return false;
                    }
                }
            }
            return ftpclient.removeDirectory(fullpath);
        } catch (IOException e) {
            Log2.log.error("Can't delete file", e);
            return false;
        }
    }

    public boolean delete() {
        if (configurator.readonly) {
            return false;
        }

        String fullpath = (root_path + path).replaceAll("//", "/");

        try {
            reconnect();
        } catch (IOException e1) {
            Log2.log.error("Can't delete file (reconnect)", e1);
            return false;
        }

        if (isDirectory()) {
            return recursiveDirectoryDelete(fullpath);
        } else {
            try {
                return ftpclient.deleteFile(fullpath);
            } catch (IOException e) {
                Log2.log.error("Can't delete file", e);
                return false;
            }
        }
    }

}