net.sourceforge.dvb.projectx.xinput.ftp.XInputFileImpl.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.dvb.projectx.xinput.ftp.XInputFileImpl.java

Source

/*
 * @(#)XInputFileImpl.java - implementation for ftp access
 *
 * Copyright (c) 2004-2006 by roehrist, All Rights Reserved. 
 * 
 * This file is part of ProjectX, a free Java based demux utility.
 * By the authors, ProjectX is intended for educational purposes only, 
 * as a non-commercial test project.
 * 
 *
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

/*
 * requires Jakarta Commons Net library, developed by the
 * Apache Software Foundation (http://www.apache.org/).
 */

package net.sourceforge.dvb.projectx.xinput.ftp;

import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.StringTokenizer;

import net.sourceforge.dvb.projectx.common.Resource;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.common.Keys;

import net.sourceforge.dvb.projectx.xinput.FileType;
import net.sourceforge.dvb.projectx.xinput.XInputFileIF;
import net.sourceforge.dvb.projectx.xinput.XInputStream;

import org.apache.commons.net.ftp.FTPFile;

import java.io.DataInputStream;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTP;

import net.sourceforge.dvb.projectx.xinput.StreamInfo;

public class XInputFileImpl implements XInputFileIF {

    private boolean debug = false;

    // Members, which are type independent
    private FileType fileType = null;

    private boolean isopen = false;

    private PushbackInputStream pbis = null;
    private File rafFile = null;
    private RandomAccessFile raf = null;
    private byte[] buffer = new byte[8];

    // Members used for type FileType.FTP
    private FtpVO ftpVO = null;

    private FTPFile ftpFile = null;

    private FTPClient client = null;

    private DataInputStream in = null;

    public XInputStream xIs = null;

    private Object constructorParameter = null;

    private StreamInfo streamInfo = null;

    private boolean supportsResume = true;

    /**
     * Private Constructor, don't use!
     */
    private XInputFileImpl() {

        throw new UnsupportedOperationException();
    }

    /**
     * Create a XInputFile of type FileType.FTP.
     * 
     * @param aFtpVO
     *          Directory data to use
     * @param aFtpFile
     *          File data to use
     */
    public XInputFileImpl(FtpVO aFtpVO) {

        if (debug)
            System.out.println("Try to create XInputFile of Type FTP");

        ftpVO = aFtpVO;
        ftpFile = aFtpVO.getFtpFile();
        fileType = FileType.FTP;

        if (!exists()) {
            throw new IllegalArgumentException("File is not of type FileType.FTP");
        }

        if (debug)
            System.out.println("Succeeded to create XInputFile of Type FTP");
    }

    /**
     * Get String representation of the object.
     * 
     * @return String representation of the object
     */
    public String toString() {

        String s;

        String name = ftpFile.getName();
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");
        name = replaceStringByString(name, "", "");

        s = "ftp://" + ftpVO.getUser() + ":" + ftpVO.getPassword() + "@" + ftpVO.getServer() + ftpVO.getPort(":")
                + ftpVO.getDirectory() + "/" + name;

        return s;
    }

    /**
     * @return String, checked of arg1 and replaced with arg2 JDK 1.2.2
     *         compatibility, replacement of newer String.replaceAll()
     */
    private String replaceStringByString(String name, String arg1, String arg2) {

        if (name == null)
            return name;

        StringBuffer sb = new StringBuffer(name);

        for (int i = 0; (i = sb.toString().indexOf(arg1, i)) != -1;)
            sb.replace(i, i + 2, arg2);

        return sb.toString();
    }

    public void setConstructorParameter(Object obj) {
        constructorParameter = obj;
    }

    public Object getConstructorParameter() {
        return constructorParameter;
    }

    /*
     * (non-Javadoc)
     * 
     * @see net.sourceforge.dvb.projectx.xinput.XInputFileIF#getFileType()
     */
    public FileType getFileType() {
        return fileType;
    }

    /**
     * Get url representation of the object.
     * 
     * @return String with url
     */
    public String getUrl() {

        String s = "ftp://" + ftpVO.getUser() + ":" + ftpVO.getPassword() + "@" + ftpVO.getServer()
                + ftpVO.getPort(":") + ftpVO.getDirectory() + "/" + ftpFile.getName();

        return s;
    }

    /**
     * Length of file in bytes.
     * 
     * @return Length of file in bytes
     */
    public long length() {

        return ftpFile.getSize();
    }

    /**
     * Time in milliseconds from the epoch. JDK1.2.2 adaption: Date.getTime()
     * 
     * @return Time in milliseconds from the epoch
     */
    public long lastModified() {

        // JDK 1.2.2 going trough Date.getTime(), Time is rounded or 0, but date
        // seems correct
        return ftpFile.getTimestamp().getTime().getTime();

        // JDK 1.4.2 return value long is not protected
        //return ftpFile.getTimestamp().getTimeInMillis();
    }

    /**
     * sets Time in milliseconds from the epoch.
     * 
     * @return success
     */
    public boolean setLastModified() {

        return true; //later
    }

    /**
     * Checks if file exists
     * 
     * @return Result of check
     */
    public boolean exists() {

        boolean b = false;

        // This method is more exact, but too expensive
        //      try {
        //         b = true;
        //         inputStream = getInputStream();
        //         inputStream.close();
        //         inputStream = null;
        //      } catch (Exception e) {
        //         b = false;
        //      }

        // If ftpFile is set, it was possible to retrieve it, so the file exists
        if (ftpFile != null) {
            b = true;
        }

        return b;
    }

    /**
     * Get Name of file
     * 
     * @return Name of file
     */
    public String getName() {

        String s = null;

        s = ftpFile.getName();
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");
        s = replaceStringByString(s, "", "");

        return s;
    }

    /**
     * Get Path of parent
     * 
     * @return Path of parent
     */
    public String getParent() {

        return ftpVO.getDirectory();
    }

    /**
     * Get input stream from the file. close() on stream closes XInputFile, too.
     * 
     * @return Input stream from the file
     */
    public InputStream getInputStream() throws FileNotFoundException, MalformedURLException, IOException {

        return getInputStream(0L);
    }

    /**
     * Get input stream from the file. close() on stream closes XInputFile, too.
     * 
     * @return Input stream from the file
     */
    public InputStream getInputStream(long start_position)
            throws FileNotFoundException, MalformedURLException, IOException {

        supportsResume = Common.getSettings().getBooleanProperty(Keys.KEY_useFtpServerResume);

        randomAccessOpen("r");

        if (supportsResume)
            client.setRestartOffset(start_position); //void

        if (debug)
            System.out.println("gIS name " + getName());

        xIs = new XInputStream(client.retrieveFileStream(ftpFile.getName()));

        if (debug)
            System.out.println("gIS retriveStream " + client.getReplyString());

        if (!supportsResume)
            xIs.skip(start_position);

        xIs.setFtpFile(this);
        return xIs;
    }

    /**
     * rename this file
     *
     * @return success
     */
    public boolean rename() throws IOException {

        //   if (isopen) { throw new IllegalStateException("XInputFile is open!"); }
        if (isopen)
            return false;

        randomAccessOpen("r");

        String name = getName();
        String newName = null;
        boolean ret = false;

        newName = Common.getGuiInterface().getUserInputDialog(name,
                Resource.getString("autoload.dialog.rename") + " " + getUrl());

        if (newName != null && !newName.equals(""))
            ret = client.rename(name, newName);

        if (ret) {
            FTPFile[] aFtpFiles = client.listFiles();

            for (int i = 0; i < aFtpFiles.length; i++)
                if (aFtpFiles[i].getName().equals(newName) && aFtpFiles[i].isFile()) {
                    ftpFile = aFtpFiles[i];
                    ftpVO.setFtpFile(ftpFile);
                    break;
                }
        }

        randomAccessClose();

        return ret;
    }

    /**
     * Opens XInputFile for random access
     * 
     * @param mode
     *          Access mode as in RandomAccessFile
     * @throws IOException
     */
    public void randomAccessOpen(String mode) throws IOException {

        if (isopen) {
            throw new IllegalStateException("XInputFile is already open!");
        }

        if (mode.compareTo("r") != 0) {
            throw new IllegalStateException("Illegal access mode for FileType.FTP");
        }

        boolean ret = false;

        client = new FTPClient();
        client.connect(ftpVO.getServer(), ftpVO.getPortasInteger()); //void
        if (debug)
            System.out.println("rAO connect " + client.getReplyString());

        ret = client.login(ftpVO.getUser(), ftpVO.getPassword()); //bool
        if (debug)
            System.out.println("rAO login " + ret + " / " + client.getReplyString());

        ret = client.changeWorkingDirectory(ftpVO.getDirectory()); //bool
        if (debug)
            System.out.println("rAO cwd " + ret + " / " + client.getReplyString());

        ret = client.setFileType(FTP.BINARY_FILE_TYPE); //bool
        if (debug)
            System.out.println("rAO binary " + ret + " / " + client.getReplyString());

        client.enterLocalPassiveMode(); //void
        if (debug)
            System.out.println("rAO PASV " + client.getReplyString());

        String[] commands = getUserFTPCommand();

        for (int i = 0; i < commands.length; i++) {
            if (commands[i] != null && commands[i].length() > 0) {
                client.sendCommand(commands[i]); //bool
                if (debug)
                    System.out.println("rAO cmd " + client.getReplyString());
            }
        }

        isopen = true;
    }

    /**
     * @throws java.io.IOException
     */
    public String[] getUserFTPCommand() {

        StringTokenizer st = new StringTokenizer(Common.getSettings().getProperty(Keys.KEY_FtpServer_Commands),
                "|");
        String[] tokens = new String[st.countTokens()];

        for (int i = 0; st.hasMoreTokens(); i++)
            tokens[i] = st.nextElement().toString().trim();

        return tokens;
    }

    private boolean EOF() throws IOException {

        int ret;

        if (in != null) {
            ret = in.read();

            if (ret < 0)
                return true;
        }

        else if (xIs != null) {
            ret = xIs.read();

            if (ret < 0)
                return true;
        }

        return false;
    }

    /**
     * @throws java.io.IOException
     */
    public void randomAccessClose() throws IOException {

        if (!isopen) {
            throw new IllegalStateException("XInputFile is already closed!");
        }

        //no need to abort a transfer explicitly, because we logout here

        boolean ret = false;
        if (debug)
            System.out.println("rAC last " + client.getReplyCode() + " / " + client.getReplyString());

        ret = client.isConnected();
        if (debug)
            System.out
                    .println("rAC isCon " + ret + " / " + client.getReplyCode() + " / " + client.getReplyString());

        /**
         * alternative kills the client instance, some server won't abort an incomplete data transfer
         * (current box-idx 80)
         */
        if (Common.getSettings().getBooleanProperty(Keys.KEY_killFtpClient)) {
            if (debug)
                System.out.println("rAC kill ");
        }

        /**
         * standard close of a client connection
         */
        else {
            if (!EOF()) {
                if (debug)
                    System.out.println("rAC !eof ");

                ret = client.abort();
                if (debug)
                    System.out.println(
                            "rAC abort " + ret + " / " + client.getReplyCode() + " / " + client.getReplyString());
            }

            ret = client.logout();
            if (debug)
                System.out.println(
                        "rAC logout " + ret + " / " + client.getReplyCode() + " / " + client.getReplyString());

            ret = client.isConnected();
            if (debug)
                System.out.println(
                        "rAC isCon " + ret + " / " + client.getReplyCode() + " / " + client.getReplyString());

            if (ret) {
                try {
                    client.disconnect();
                    if (debug)
                        System.out.println("rAC disc " + client.getReplyCode() + " / " + client.getReplyString());
                } catch (IOException e) {
                    if (debug)
                        System.out.println("rAC disc-er " + e);
                }
            }
        }

        in = null;
        xIs = null;
        client = null;
        isopen = false;

        System.gc();

        if (debug)
            System.out.println("rAC out ");
    }

    /**
     * @param aPosition
     *          The offset position, measured in bytes from the beginning of the
     *          file, at which to set the file pointer.
     * @throws java.io.IOException
     */
    public void randomAccessSeek(long aPosition) throws IOException {

        client.setRestartOffset(aPosition); //void
        if (debug)
            System.out.println("rAS REST " + client.getReplyString() + " /aP " + aPosition);

        in = new DataInputStream(client.retrieveFileStream(ftpFile.getName()));
        if (debug)
            System.out.println("rAS retriveStream " + client.getReplyString());
    }

    /**
     * @return @throws
     *         IOException
     */
    public long randomAccessGetFilePointer() throws IOException {

        return raf.getFilePointer();
    }

    /**
     * @return @throws
     *         IOException
     */
    public int randomAccessRead() throws IOException {

        buffer[0] = -1;
        randomAccessRead(buffer, 0, 1);
        return (int) buffer[0];
    }

    /**
     * @param aBuffer
     *          The buffer into which the data is read.
     * @return @throws
     *         java.io.IOException
     */
    public int randomAccessRead(byte[] aBuffer) throws IOException {
        return randomAccessRead(aBuffer, 0, aBuffer.length);
    }

    /**
     * @param aBuffer
     *          The buffer into which the data is written.
     * @param aOffset
     *          The offset at which the data should be written.
     * @param aLength
     *          The amount of data to be read.
     * @return @throws
     *         IOException
     */
    public int randomAccessRead(byte[] aBuffer, int aOffset, int aLength) throws IOException {

        int bytesRead, totalBytes = aOffset;

        while ((bytesRead = in.read(aBuffer, totalBytes, aLength - totalBytes)) > 0)
            totalBytes += bytesRead;

        return bytesRead;
    }

    /**
     * @return Read line
     * @throws IOException
     */
    public String randomAccessReadLine() throws IOException {

        return in.readLine();
    }

    /**
     * @param aBuffer
     *          The data.
     * @throws java.io.IOException
     */
    public void randomAccessWrite(byte[] aBuffer) throws IOException {

        throw new IllegalStateException("Illegal access for FileType.FTP");
    }

    /**
     * Convinience method for a single random read access to a input file. The
     * file is opened before and closed after read.
     * 
     * @param aBuffer
     *          Buffer to fill with read bytes (up to aBuffer.length() bytes)
     * @param aPosition
     *          Fileposition at which we want read
     * @throws IOException
     */
    public void randomAccessSingleRead(byte[] aBuffer, long aPosition) throws IOException {

        randomAccessOpen("r");
        randomAccessSeek(aPosition);
        randomAccessRead(aBuffer);
        randomAccessClose();
    }

    /**
     * @return Long value read.
     * @throws java.io.IOException
     */
    public long randomAccessReadLong() throws IOException {

        return in.readLong();
    }

    //
    /**
     *
     */
    public void setStreamInfo(StreamInfo _streamInfo) {
        streamInfo = _streamInfo;
    }

    /**
     *
     */
    public StreamInfo getStreamInfo() {
        return streamInfo;
    }
}