ch.cyberduck.core.dav.DAVPath.java Source code

Java tutorial

Introduction

Here is the source code for ch.cyberduck.core.dav.DAVPath.java

Source

package ch.cyberduck.core.dav;

/*
 * Copyright (c) 2002-2011 David Kocher. All rights reserved.
 *
 * http://cyberduck.ch/
 *
 * 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.
 *
 * Bug fixes, suggestions and comments should be sent to:
 * dkocher@cyberduck.ch
 */

import ch.cyberduck.core.AbstractPath;
import ch.cyberduck.core.AttributedList;
import ch.cyberduck.core.Path;
import ch.cyberduck.core.Preferences;
import ch.cyberduck.core.StreamListener;
import ch.cyberduck.core.http.DelayedHttpEntityCallable;
import ch.cyberduck.core.http.HttpPath;
import ch.cyberduck.core.http.ResponseOutputStream;
import ch.cyberduck.core.i18n.Locale;
import ch.cyberduck.core.io.BandwidthThrottle;
import ch.cyberduck.core.io.IOResumeException;
import ch.cyberduck.core.local.Local;
import ch.cyberduck.core.transfer.TransferStatus;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.googlecode.sardine.DavResource;

/**
 * @version $Id: DAVPath.java 10837 2013-04-10 20:42:42Z dkocher $
 */
public class DAVPath extends HttpPath {
    private static Logger log = Logger.getLogger(DAVPath.class);

    private final DAVSession session;

    public DAVPath(DAVSession s, String parent, String name, int type) {
        super(parent, name, type);
        this.session = s;
    }

    public DAVPath(DAVSession s, String path, int type) {
        super(path, type);
        this.session = s;
    }

    public DAVPath(DAVSession s, String parent, Local file) {
        super(parent, file);
        this.session = s;
    }

    public <T> DAVPath(DAVSession s, T dict) {
        super(dict);
        this.session = s;
    }

    @Override
    public DAVSession getSession() {
        return session;
    }

    @Override
    public void readSize() {
        if (this.attributes().isFile()) {
            try {
                this.getSession().check();
                this.getSession().message(MessageFormat
                        .format(Locale.localizedString("Getting size of {0}", "Status"), this.getName()));

                this.readAttributes();
            } catch (IOException e) {
                log.warn("Cannot read file attributes");
            }
        }
    }

    @Override
    public void readTimestamp() {
        if (this.attributes().isFile()) {
            try {
                this.getSession().check();
                this.getSession().message(MessageFormat
                        .format(Locale.localizedString("Getting timestamp of {0}", "Status"), this.getName()));

                this.readAttributes();
            } catch (IOException e) {
                log.warn("Cannot read file attributes");
            }
        }
    }

    private void readAttributes() throws IOException {
        final List<DavResource> resources = this.getSession().getClient().list(this.toURL());
        for (final DavResource resource : resources) {
            this.readAttributes(resource);
        }
    }

    private void readAttributes(DavResource resource) {
        if (resource.getModified() != null) {
            this.attributes().setModificationDate(resource.getModified().getTime());
        }
        if (resource.getCreation() != null) {
            this.attributes().setCreationDate(resource.getCreation().getTime());
        }
        if (resource.getContentLength() != null) {
            this.attributes().setSize(resource.getContentLength());
        }
        this.attributes().setChecksum(resource.getEtag());
        this.attributes().setETag(resource.getEtag());
    }

    @Override
    public boolean exists() {
        if (super.exists()) {
            return true;
        }
        if (this.attributes().isDirectory()) {
            // Parent directory may not be accessible. Issue #5662
            try {
                return this.getSession().getClient().exists(this.toURL());
            } catch (IOException e) {
                this.error("Cannot read file attributes", e);
            }
        }
        return false;
    }

    @Override
    public void delete() {
        try {
            this.getSession().check();
            this.getSession().message(
                    MessageFormat.format(Locale.localizedString("Deleting {0}", "Status"), this.getName()));

            this.getSession().getClient().delete(this.toURL());
        } catch (IOException e) {
            this.error("Cannot delete {0}", e);
        }
    }

    @Override
    public AttributedList<Path> list(final AttributedList<Path> children) {
        try {
            this.getSession().check();
            this.getSession().message(MessageFormat
                    .format(Locale.localizedString("Listing directory {0}", "Status"), this.getName()));

            final List<DavResource> resources = this.getSession().getClient().list(this.toURL());
            for (final DavResource resource : resources) {
                // Try to parse as RFC 2396
                final URI uri = resource.getHref();
                DAVPath p = new DAVPath(this.getSession(), uri.getPath(),
                        resource.isDirectory() ? DIRECTORY_TYPE : FILE_TYPE);
                p.setParent(this);
                p.readAttributes(resource);
                children.add(p);
            }
        } catch (IOException e) {
            log.warn("Listing directory failed:" + e.getMessage());
            children.attributes().setReadable(false);
        }
        return children;
    }

    @Override
    public void mkdir() {
        try {
            this.getSession().check();
            this.getSession().message(
                    MessageFormat.format(Locale.localizedString("Making directory {0}", "Status"), this.getName()));

            this.getSession().getClient().createDirectory(this.toURL());
        } catch (IOException e) {
            this.error("Cannot create folder {0}", e);
        }
    }

    @Override
    public void readMetadata() {
        if (attributes().isFile()) {
            try {
                this.getSession().check();
                this.getSession().message(MessageFormat
                        .format(Locale.localizedString("Reading metadata of {0}", "Status"), this.getName()));

                final List<DavResource> resources = this.getSession().getClient().list(this.toURL());
                for (DavResource resource : resources) {
                    this.attributes().setMetadata(resource.getCustomProps());
                }
            } catch (IOException e) {
                this.error("Cannot read file attributes", e);
            }
        }
    }

    @Override
    public void writeMetadata(Map<String, String> meta) {
        if (attributes().isFile()) {
            try {
                this.getSession().check();
                this.getSession().message(MessageFormat
                        .format(Locale.localizedString("Writing metadata of {0}", "Status"), this.getName()));

                this.getSession().getClient().setCustomProps(this.toURL(), meta,
                        Collections.<java.lang.String>emptyList());
            } catch (IOException e) {
                this.error("Cannot write file attributes", e);
            } finally {
                this.attributes().clear(false, false, false, true);
            }
        }
    }

    @Override
    public void rename(AbstractPath renamed) {
        try {
            this.getSession().check();
            this.getSession().message(MessageFormat.format(Locale.localizedString("Renaming {0} to {1}", "Status"),
                    this.getName(), renamed.getName()));

            this.getSession().getClient().move(this.toURL(), renamed.toURL());
        } catch (IOException e) {
            this.error("Cannot rename {0}", e);
        }
    }

    @Override
    public void copy(AbstractPath copy, BandwidthThrottle throttle, StreamListener listener,
            final TransferStatus status) {
        if (((Path) copy).getSession().equals(this.getSession())) {
            // Copy on same server
            try {
                this.getSession().check();
                this.getSession().message(MessageFormat
                        .format(Locale.localizedString("Copying {0} to {1}", "Status"), this.getName(), copy));

                if (attributes().isFile()) {
                    this.getSession().getClient().copy(this.toURL(), copy.toURL());
                    listener.bytesSent(this.attributes().getSize());
                    status.setComplete();
                }
            } catch (IOException e) {
                this.error("Cannot copy {0}", e);
            }
        } else {
            // Copy to different host
            super.copy(copy, throttle, listener, status);
        }
    }

    @Override
    public InputStream read(final TransferStatus status) throws IOException {
        Map<String, String> headers = new HashMap<String, String>();
        if (status.isResume()) {
            headers.put(HttpHeaders.RANGE, "bytes=" + status.getCurrent() + "-");
        }
        return this.getSession().getClient().get(this.toURL(), headers);
    }

    @Override
    public void download(final BandwidthThrottle throttle, final StreamListener listener,
            final TransferStatus status) {
        OutputStream out = null;
        InputStream in = null;
        try {
            in = this.read(status);
            out = this.getLocal().getOutputStream(status.isResume());
            this.download(in, out, throttle, listener, status);
        } catch (IOException e) {
            this.error("Download failed", e);
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        }
    }

    @Override
    public void upload(final BandwidthThrottle throttle, final StreamListener listener,
            final TransferStatus status) {
        try {
            InputStream in = null;
            ResponseOutputStream<Void> out = null;
            try {
                in = this.getLocal().getInputStream();
                if (status.isResume()) {
                    long skipped = in.skip(status.getCurrent());
                    log.info(String.format("Skipping %d bytes", skipped));
                    if (skipped < status.getCurrent()) {
                        throw new IOResumeException(
                                String.format("Skipped %d bytes instead of %d", skipped, status.getCurrent()));
                    }
                }
                out = this.write(status);
                this.upload(out, in, throttle, listener, status);
            } finally {
                IOUtils.closeQuietly(in);
                IOUtils.closeQuietly(out);
            }
            if (null != out) {
                out.getResponse();
            }
        } catch (IOException e) {
            this.error("Upload failed", e);
        }
    }

    @Override
    public ResponseOutputStream<Void> write(final TransferStatus status) throws IOException {
        final Map<String, String> headers = new HashMap<String, String>();
        if (status.isResume()) {
            headers.put(HttpHeaders.CONTENT_RANGE,
                    "bytes " + status.getCurrent() + "-" + (status.getLength() - 1) + "/" + status.getLength());
        }
        if (Preferences.instance().getBoolean("webdav.expect-continue")) {
            headers.put(HTTP.EXPECT_DIRECTIVE, HTTP.EXPECT_CONTINUE);
        }
        try {
            return this.write(headers, status);
        } catch (HttpResponseException e) {
            if (e.getStatusCode() == HttpStatus.SC_EXPECTATION_FAILED) {
                // Retry with the Expect header removed
                headers.remove(HTTP.EXPECT_DIRECTIVE);
                return this.write(headers, status);
            } else {
                throw e;
            }
        }
    }

    private ResponseOutputStream<Void> write(final Map<String, String> headers, final TransferStatus status)
            throws IOException {
        // Submit store call to background thread
        final DelayedHttpEntityCallable<Void> command = new DelayedHttpEntityCallable<Void>() {
            /**
             *
             * @return The ETag returned by the server for the uploaded object
             */
            @Override
            public Void call(AbstractHttpEntity entity) throws IOException {
                getSession().getClient().put(toURL(), entity, headers);
                return null;
            }

            @Override
            public long getContentLength() {
                return status.getLength() - status.getCurrent();
            }
        };
        return this.write(command);
    }

    @Override
    public String toURL() {
        if (this.attributes().isDirectory()) {
            return super.toURL() + Path.DELIMITER;
        }
        return super.toURL();
    }

    @Override
    public String toHttpURL() {
        return this.toURL();
    }
}