fi.hip.sicx.webdav.WebdavClient.java Source code

Java tutorial

Introduction

Here is the source code for fi.hip.sicx.webdav.WebdavClient.java

Source

/*
 * Copyright (C) 2012 Helsinki Institute of Physics, University of Helsinki
 * All rights reserved. See the copyright.txt in the distribution for a full 
 * listing of individual contributors.
 *    Licensed 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 fi.hip.sicx.webdav;

import static org.junit.Assert.assertTrue;
import fi.hip.sicx.store.StorageClient;
import fi.hip.sicx.store.StorageIOException;
import fi.hip.sicx.store.StorageClientObserver;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.ConflictException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.exceptions.NotFoundException;
import io.milton.httpclient.Folder;
import io.milton.httpclient.Host;
import io.milton.httpclient.HttpException;
import io.milton.httpclient.Resource;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.input.CountingInputStream;

/**
 * WebdavClient class.
 *
 * @author Seppo Heikkila <seppo.heikkila@cern.ch>
 */
public class WebdavClient implements StorageClient {

    public Host host = null;
    private Future<?> future = null;

    private String address = null;
    private int port = 80;
    private String username = null;
    private String password = null;
    private String path = null;
    private Resource pr = null;

    private int datasize = 0;
    private CountingInputStream cis = null;

    // These enable non-blocking uploading
    private ExecutorService myExecutor = null;
    private io.milton.httpclient.File uploadedFile = null;

    public WebdavClient(String webdav_address, int webdav_port, String webdav_username, String webdav_password,
            String webdav_path) {
        this.address = webdav_address;
        this.port = webdav_port;
        this.username = webdav_username;
        this.password = webdav_password;
        this.path = webdav_path;
        this.pr = null;
    }

    /**
     * Generates valid filename for uploading. Constraints are
     * 1) Size max 29 characters (google drive)
     * 2) There has to be valid ending, otherwise it is assigned (google drive)
     * 3) Remove "-" chars, so that they do not cause trouble (optional)
     * 4) The filename should be unique, e.g. every second letter (optional)
     * 5) Two last letters have to stay same
     * @param inpath Original name.
     * @return Modified name.
     */
    public String generateValidStorageFilename(String inname) {
        String retme = null;
        if (inname == null) {
            return null;
        }

        // 3) Remove special chars, at least '-'
        retme = inname;
        retme = retme.replace('-', 'S');

        // 4) Take every 2nd letter
        if (retme.length() >= 25) {
            String newretme = retme.substring(0, 4);
            for (int i = 4; retme.length() > i; i += 2) {
                newretme += retme.substring(i, i + 1);
            }
            retme = newretme;
        }

        // 5
        retme += inname.substring(inname.length() - 2, inname.length());

        // 1) Take only 25 chars, 5) last ones
        if (retme.length() > 25) {
            retme = retme.substring(retme.length() - 25);
        }

        // 2)
        retme += ".bin"; // This is needed, or sometimes files might have autogenerated ending .html etc.

        return retme;
    }

    /**
     * Checks if we have valid path. Path is valid if path
     * and pr are not null.
     * 
     * @param inpath Path to be checked. Any slashes mean that there is path also.
     * @return The filename without path.
     */
    private String refreshUploadPath(String inpath) {
        String retme = null;

        // Check if we have a path update
        if (inpath != null && inpath.lastIndexOf('/') != -1) {
            retme = inpath.substring(inpath.lastIndexOf('/') + 1);
            path = inpath.substring(0, inpath.lastIndexOf('/'));
            System.out.println("Inpath: '" + inpath + "'.");
            System.out.println("Path: '" + path + "'.");
            System.out.println("File: '" + retme + "'.");
            pr = null;
        } else {
            retme = inpath;
        }

        // Check if given path is valid
        if (path != null && pr == null) {
            try {
                pr = host.find(path);
            } catch (NotAuthorizedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (BadRequestException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (HttpException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            if (pr == null || !(pr instanceof Folder)) {
                path = null;
                pr = null;
            }
        }

        return retme;
    }

    @SuppressWarnings("unused")
    private boolean tryConnect() {
        // Connect
        this.host = new Host(this.address, null, this.port, this.username, this.password, null, 3000000, null,
                null);

        try {
            List<? extends Resource> lr = this.host.children();
            if (lr != null) {
                return true;
            }
        } catch (IOException e) {
            System.out.println("We would need longer timeout: " + e);
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public boolean connect() {
        // Check parameters
        if (this.address == null) {
            return false;
        }
        if (this.port <= 0) {
            return false;
        }

        myExecutor = Executors.newSingleThreadExecutor();

        // Lets set timeout to 5 minutes
        this.host = new Host(this.address, null, this.port, this.username, this.password, null, 300000, null, null);
        // Lets check if the connection works
        try {
            List<? extends Resource> lr = this.host.children();
            if (lr == null) {
                return false;
            }
        } catch (Exception e) {
            return false;
        }

        refreshUploadPath(null);

        // Check success
        if (this.host != null) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean storeFile(String localInputFilename, String fileInTheCloud, StorageClientObserver sco) {
        try {
            java.io.File f = new java.io.File(localInputFilename);
            FileInputStream fis = new FileInputStream(f);
            OutputStream out = writeData(fileInTheCloud, (int) f.length(), null);
            do {
                int d8b = fis.read();
                if (d8b != -1) {
                    out.write(d8b);
                } else {
                    break;
                }
            } while (true);
            fis.close();
            out.close(); // must - otherwise file will not be uploaded
            // We have to wait for the upload to be completed
            while (writeDataIsCompleted() == false) {
                Thread.sleep(10);
            }

        } catch (StorageIOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if (uploadedFile == null) {
            return false;
        }

        return true;
    }

    @Override
    public boolean getFile(String cloudFile, String localOutFile, StorageClientObserver sco) {
        // TODO Auto-generated method stub
        return false;
    }

    /**
     * Waits for the file upload to finish. If the time period is
     * exceeded, the upload is terminated.
     * 
     * @param time_in_seconds Time in seconds to wait for the upload to finish
     * @return Resource to the uploaded file
     * @deprecated replaced by {@link #writeDataWaitToComplete(int)}
     */
    @SuppressWarnings("unused")
    @Deprecated
    private Resource waitWriteDataToFinish(int time_in_seconds) {
        try {
            myExecutor.awaitTermination(time_in_seconds, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        if (uploadedFile == null) {
            return null;
        }

        return uploadedFile;
    }

    DataOutputStream dos = null; // This is needed to know the number of bytes written

    @Override
    public OutputStream writeData(String fileInTheCloud, int indatasize, StorageClientObserver sco)
            throws StorageIOException {

        this.datasize = indatasize; // For calculating transfer progress
        //PipedOutputStream pos = null;

        // Parameters for the Thread call (final needed for parameters of the thread)
        final String paramfileInTheCloud = fileInTheCloud;
        final int paramindatasize = indatasize;
        final Host paramhost = host;
        final PipedInputStream ins = new PipedInputStream();
        try {
            this.dos = new DataOutputStream(new PipedOutputStream(ins));
        } catch (IOException e1) {
            throw new StorageIOException("Could not create pipeoutputstream.");
        }

        // Uploading is blocking so we need to execute it on separate thread
        this.future = myExecutor.submit(new Runnable() {
            public void run() {
                String justFileName = "INVALID-NAME.txt";
                try {
                    justFileName = refreshUploadPath(paramfileInTheCloud);
                    justFileName = generateValidStorageFilename(justFileName);
                    if (path != null && pr != null) {
                        uploadedFile = ((Folder) pr).upload(justFileName, ins, (long) paramindatasize, null);
                    } else {
                        uploadedFile = paramhost.upload(justFileName, ins, (long) paramindatasize, null);
                    }
                } catch (NotAuthorizedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (ConflictException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (BadRequestException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (NotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (HttpException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("DONE: " + justFileName);
            }
        });

        return this.dos;
    }

    @Override
    public InputStream readData(String fileInTheCloud, int indatasize, StorageClientObserver sco) {

        FileInputStream fis = null;
        try {
            // FXIME: There should be more elegant solutions than downloading to temp file
            String pathlocal = "/tmp/";
            String body = "SICX_WebdavClient_readData";
            String ending = ".tmp";
            File dir = new File(pathlocal);
            File f = File.createTempFile(body, ending, dir);

            fileInTheCloud = refreshUploadPath(fileInTheCloud);
            fileInTheCloud = generateValidStorageFilename(fileInTheCloud);
            Resource cloudfile = null;
            if (path != null && pr != null) {
                cloudfile = host.find(path + File.separator + fileInTheCloud);
            } else {
                cloudfile = host.find(fileInTheCloud);
            }
            if (cloudfile == null) {
                return null;
            }

            cloudfile.downloadTo(f, null);

            fis = new FileInputStream(f);
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return fis;
    }

    @Override
    public boolean checkFile(String cloudFile, StorageClientObserver sco) {
        System.out.println("Checking file: " + cloudFile);
        try {
            cloudFile = refreshUploadPath(cloudFile);
            cloudFile = generateValidStorageFilename(cloudFile);
            Resource cloudfile = null;
            if (path != null && pr != null) {
                System.out.println("Checking file1: " + path + File.separator + cloudFile);
                cloudfile = host.find(path + File.separator + cloudFile);
                if (cloudfile == null) { // Lets see if the non-finding was due to invalid cache
                    cloudfile = host.find(path + File.separator + cloudFile, true);
                }
            } else {
                System.out.println("Checking file2: " + cloudFile);
                cloudfile = host.find(cloudFile);
                if (cloudfile == null) { // Lets see if the non-finding was due to invalid cache
                    cloudfile = host.find(cloudFile, true);
                }
            }
            if (cloudfile != null) {
                return true;
            }
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public boolean deleteFile(String cloudFile, StorageClientObserver sco) {
        System.out.println("Deleting file: " + cloudFile);
        try {
            cloudFile = refreshUploadPath(cloudFile);
            cloudFile = generateValidStorageFilename(cloudFile);
            Resource cloudfile = null;
            if (path != null && pr != null) {
                cloudfile = host.find(path + File.separator + cloudFile);
            } else {
                cloudfile = host.find(cloudFile);
            }
            if (cloudfile != null) {
                cloudfile.delete();
                return true;
            }
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ConflictException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return true; // Delete always works! :)
        //return false;
    }

    @Override
    public boolean logout() {
        return true;
    }

    @Override
    public int getTransferProgress() {
        double retme = 0;
        if (this.dos != null && this.datasize > 0) {
            retme = 100.0 * dos.size() * 1.0 / (1.0 * this.datasize);
        } else if (this.cis != null && this.datasize > 0) {
            retme = 100.0 * cis.getCount() * 1.0 / (1.0 * this.datasize);
        }

        return (int) retme;
    }

    @Override
    public boolean writeDataWaitToComplete(int timeout_ms) {
        try {
            int sleeptime = 10;
            while (this.future.get() != null || timeout_ms <= 0) {
                //System.out.println("Waiting thread to finish.");
                Thread.sleep(sleeptime);
                timeout_ms -= sleeptime;
            }
            if (timeout_ms <= 0) {
                return false;
            }
            return true;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
    }

    @Override
    public boolean writeDataIsCompleted() {
        try {
            if (this.future.get() == null) {
                return true;
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ExecutionException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return false;
        //return myExecutor.isTerminated();
    }

    String uriPath = "webdav:///";

    @Override
    public String getURI(String path) {
        return uriPath + path;
    }

    @Override
    public String setURI(String newURIStart) {
        uriPath += newURIStart;
        return uriPath;
    }

    @Override
    public String getType() {
        return "webdav";
    }

    @Override
    public String getVersion() {
        return "1.0";
    }

    @Override
    public boolean isReusable() {
        return true;
        //return false;
    }

    /**
     * Update the path which we are accessing/manipulating.
     * 
     * @param webdav_path Path to the directory that is being referred.
     * @return true if all ok, else false
     */
    public boolean setPath(String webdav_path) {
        this.path = webdav_path;
        this.pr = null;
        refreshUploadPath(null);

        return true;
    }

    /**
     * Get names of all the children in the current directory.
     * @return Vector containing all the names
     */
    public Vector<String> getChildrenDirectories() {
        Vector<String> retme = new Vector<String>();

        try {
            refreshUploadPath(null);
            if (path != null && pr != null) {
                if (pr instanceof Folder) {
                    for (Resource r : ((Folder) pr).children()) {
                        if (r instanceof Folder) {
                            retme.add(r.name);
                        }
                    }
                }
            } else {
                for (Resource r : host.children()) {
                    if (r instanceof Folder) {
                        retme.add(r.name);
                    }
                }
            }
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return retme;
    }

    //////////////////////////////////////////////////////////
    ///////// Code that is not useful at this point //////////
    //////////////////////////////////////////////////////////

    //@Test
    @SuppressWarnings("unused")
    private void testCreateDir() throws Exception {
        boolean exception = false;

        printHost();

        Resource fold = findResource("Files/Elaimet2/kissa");
        if (fold == null) {
            System.out.println("Target not found.");
        }

        fold = findResource("Files/Elaimet/kissa");
        System.out.println("Found: " + fold.displayName);

        // Test that null directory name fails
        exception = false;
        try {
            Folder test_folder = createDirectory(null, null);
        } catch (BadRequestException e) {
            exception = true;
        }
        assertTrue(exception);

        // Test that creating an existing directory fails
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", "Files/Elaimet");
            test_folder = createDirectory("siiamilais4", "Files/Elaimet");
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", null);
            test_folder = createDirectory("siiamilais4", null);
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);

        // Test that invalid path fails
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", "I_Hope_There_is_Not_this_Directory/Here");
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);

        printHost();

        // Upload a file to the new folder
        //java.io.File presents = new File("presents.xls");
        //xmasShopping.upload(presents);
        //Folder myDocs = (Folder)host.child("koe"); // look up the My Documents folder

    }

    private void printDirectory(Folder f, int depth)
            throws NotAuthorizedException, BadRequestException, IOException, HttpException {
        for (Resource r : f.children()) {
            // Resource is base class for File and Folder
            String s = "";
            if (depth > 1) {
                s = String.format("%1$" + (depth - 1) * 3 + "s", "");

            }
            s += "\\--";
            if (r instanceof Folder) {
                System.out.println(s + r.name + "/ (" + r.getClass() + ")");
                printDirectory((Folder) r, depth + 1);
            } else {
                System.out.println(s + r.name + " (" + r.getClass() + ")");
            }
        }
    }

    private void printHost() {
        try {
            for (Resource r : host.children()) {
                // Resource is base class for File and Folder
                if (r instanceof Folder) {
                    System.out.println(r.name + "/ (" + r.getClass() + ")");
                    printDirectory((Folder) r, 1);
                } else {
                    System.out.println(r.name + " (" + r.getClass() + ")");
                }
            }
        } catch (NotAuthorizedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (BadRequestException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (HttpException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public Resource findResource(String path)
            throws NotAuthorizedException, BadRequestException, IOException, HttpException {
        Resource retme = null;

        StringTokenizer st = new StringTokenizer(path, "/");

        // Find root first
        if (st.hasMoreTokens()) {
            String token = st.nextToken();
            for (Resource r : host.children()) {
                if (r.name.equals(token)) {
                    retme = r;
                }
            }
        }

        // Then rest of the path
        while (st.hasMoreTokens()) {
            if (retme == null || !(retme instanceof Folder)) {
                return null;
            }
            String token = st.nextToken();
            boolean nothing_found = true;
            for (Resource r : ((Folder) retme).children()) {
                if (r.name.equals(token)) {
                    retme = r;
                    nothing_found = false;
                }
            }
            if (nothing_found)
                return null;
        }

        return retme;
    }

    private Folder createDirectory(String dirname, String path) throws NotAuthorizedException, ConflictException,
            BadRequestException, NotFoundException, IOException, HttpException {
        Folder retme = null;

        if (dirname == null || dirname.length() == 0) {
            throw new BadRequestException("mkdir: cannot create directory without name.");
        }

        // If no path is given, lets create the directory at root
        if (path == null) {
            if (host.child(dirname) != null) {
                throw new IOException("mkdir: cannot create directory '" + dirname + "': Folder exists.");
            }
            return host.createFolder(dirname);
        }

        // Otherwise, find target folder and create directory there
        Resource fold = findResource(path);
        if (fold != null && fold instanceof Folder) {
            // Check if it exist already
            if (((Folder) fold).child(dirname) != null) {
                throw new IOException("mkdir: cannot create directory '" + dirname + "': Folder exists.");
            }
            retme = ((Folder) fold).createFolder(dirname);
        } else {
            throw new IOException("mkdir: cannot create directory '" + path + "': No such file or directory.");
        }

        return retme;
    }

    //@Test
    @SuppressWarnings("unused")
    private void testUploadFile() throws Exception {
        boolean exception = false;

        // Lets create example input
        String str = "This is a String ~ GoGoGo";
        InputStream is = new ByteArrayInputStream(str.getBytes());
        BufferedReader br = new BufferedReader(new InputStreamReader(is));

        //this.connect("localhost/oc451/remote.php/webdav", 80, "sicx", "sicx");

        printHost();

        Resource fold = findResource("Files/Elaimet2/kissa");
        if (fold == null) {
            System.out.println("Target not found.");
        }

        fold = findResource("Files/Elaimet/kissa");
        System.out.println("Found: " + fold.displayName);

        // Test that null directory name fails
        exception = false;
        try {
            Folder test_folder = createDirectory(null, null);
        } catch (BadRequestException e) {
            exception = true;
        }
        assertTrue(exception);

        // Test that creating an existing directory fails
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", "Files/Elaimet");
            test_folder = createDirectory("siiamilais4", "Files/Elaimet");
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", null);
            test_folder = createDirectory("siiamilais4", null);
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);

        // Test that invalid path fails
        exception = false;
        try {
            Folder test_folder = createDirectory("siiamilais4", "I_Hope_There_is_Not_this_Directory/Here");
        } catch (IOException e) {
            exception = true;
        }
        assertTrue(exception);

        printHost();

    }
}