org.cryptomator.frontend.webdav.jackrabbitservlet.DavFolder.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptomator.frontend.webdav.jackrabbitservlet.DavFolder.java

Source

/*******************************************************************************
 * Copyright (c) 2015, 2016 Sebastian Stenzel and others.
 * This file is licensed under the terms of the MIT license.
 * See the LICENSE.txt file for more info.
 *
 * Contributors:
 *     Sebastian Stenzel - initial API and implementation
 *******************************************************************************/
package org.cryptomator.frontend.webdav.jackrabbitservlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.time.Instant;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavResourceIterator;
import org.apache.jackrabbit.webdav.DavResourceIteratorImpl;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.lock.ActiveLock;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.ResourceType;
import org.cryptomator.common.streams.AutoClosingStream;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.Node;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.filesystem.jackrabbit.FileLocator;
import org.cryptomator.filesystem.jackrabbit.FolderLocator;

import com.google.common.io.ByteStreams;

class DavFolder extends DavNode<FolderLocator> {

    private static final DavPropertyName PROPERTY_QUOTA_AVAILABLE = DavPropertyName.create("quota-available-bytes");
    private static final DavPropertyName PROPERTY_QUOTA_USED = DavPropertyName.create("quota-used-bytes");

    public DavFolder(FilesystemResourceFactory factory, LockManager lockManager, DavSession session,
            FolderLocator folder) {
        super(factory, lockManager, session, folder);
        properties.add(new ResourceType(ResourceType.COLLECTION));
        properties.add(new DefaultDavProperty<Integer>(DavPropertyName.ISCOLLECTION, 1));
    }

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

    @Override
    public void spool(OutputContext outputContext) throws IOException {
        // no-op
    }

    @Override
    public void addMember(DavResource resource, InputContext inputContext) throws DavException {
        if (resource instanceof DavFolder) {
            addMemberFolder((DavFolder) resource);
        } else if (resource instanceof DavFile) {
            assert inputContext.hasStream();
            addMemberFile((DavFile) resource, inputContext.getInputStream());
        } else {
            throw new IllegalArgumentException("Unsupported resource type: " + resource.getClass().getName());
        }
    }

    private void addMemberFolder(DavFolder memberFolder) {
        node.folder(memberFolder.getDisplayName()).create();
    }

    private void addMemberFile(DavFile memberFile, InputStream inputStream) {
        try (ReadableByteChannel src = Channels.newChannel(inputStream);
                WritableFile dst = node.file(memberFile.getDisplayName()).openWritable()) {
            dst.truncate();
            ByteStreams.copy(src, dst);
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    @Override
    public DavResourceIterator getMembers() {
        final Stream<DavFolder> folders = node.folders().map(this::folderToDavFolder);
        final Stream<DavFile> files = node.files().map(this::fileToDavFile);
        final Stream<DavResource> members = AutoClosingStream.from(Stream.concat(folders, files));
        return new DavResourceIteratorImpl(members.collect(Collectors.toList()));
    }

    private DavFolder folderToDavFolder(FolderLocator memberFolder) {
        return factory.createFolder(memberFolder, session);
    }

    private DavFile fileToDavFile(FileLocator memberFile) {
        return factory.createFile(memberFile, session);
    }

    @Override
    public void removeMember(DavResource member) throws DavException {
        for (ActiveLock lock : member.getLocks()) {
            member.unlock(lock.getToken());
        }
        final Node child = getMemberNode(member.getDisplayName());
        child.delete();
    }

    /**
     * @throws DavException
     *             Error 404 if no child with the given name exists
     */
    private Node getMemberNode(String name) throws DavException {
        Node file = node.file(name);
        Node folder = node.folder(name);
        if (file.exists()) {
            return file;
        } else if (folder.exists()) {
            return folder;
        } else {
            throw new DavException(DavServletResponse.SC_NOT_FOUND,
                    "No such file or directory: " + node.getResourcePath() + name);
        }
    }

    @Override
    public void move(DavResource destination) throws DavException {
        if (destination instanceof DavFolder) {
            DavFolder dst = (DavFolder) destination;
            if (dst.node.exists()) {
                dst.node.delete();
            } else if (!dst.node.parent().get().exists()) {
                throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
            }
            node.moveTo(dst.node);
        } else if (destination instanceof DavFile) {
            DavFile dst = (DavFile) destination;
            Folder parent = dst.node.parent().get();
            Folder newDst = parent.folder(dst.node.name());
            if (dst.node.exists()) {
                dst.node.delete();
            } else if (!parent.exists()) {
                throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
            }
            node.moveTo(newDst);
        } else {
            throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
        }
    }

    @Override
    public void copy(DavResource destination, boolean shallow) throws DavException {
        if (!node.exists()) {
            throw new DavException(DavServletResponse.SC_NOT_FOUND);
        }
        if (destination instanceof DavNode) {
            DavNode<?> dst = (DavNode<?>) destination;
            if (!dst.node.parent().get().exists()) {
                throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
            }
        }
        if (destination instanceof DavFolder) {
            DavFolder dst = (DavFolder) destination;
            if (shallow) {
                // create destination, if it doesn't exist yet:
                dst.node.create();
                // http://www.webdav.org/specs/rfc2518.html#copy.for.collections
                node.creationTime().ifPresent(dst::setCreationTime);
                dst.setModificationTime(node.lastModified());
            } else {
                node.copyTo(dst.node);
            }
        } else if (destination instanceof DavFile) {
            DavFile dst = (DavFile) destination;
            Folder parent = dst.node.parent().get();
            Folder newDst = parent.folder(dst.node.name());
            if (dst.node.exists()) {
                dst.node.delete();
            }
            node.copyTo(newDst);
        } else {
            throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
        }
    }

    @Override
    protected void setModificationTime(Instant instant) {
        node.setLastModified(instant);
    }

    @Override
    protected void setCreationTime(Instant instant) {
        node.setCreationTime(instant);
    }

    @Override
    public DavPropertyName[] getPropertyNames() {
        return ArrayUtils.addAll(super.getPropertyNames(), PROPERTY_QUOTA_AVAILABLE, PROPERTY_QUOTA_USED);
    }

    @Override
    public DavProperty<?> getProperty(DavPropertyName name) {
        if (PROPERTY_QUOTA_AVAILABLE.equals(name)) {
            return node.fileSystem().quotaAvailableBytes()
                    .map(numBytes -> new DefaultDavProperty<Long>(name, numBytes)).orElse(null);
        } else if (PROPERTY_QUOTA_USED.equals(name)) {
            return node.fileSystem().quotaUsedBytes().map(numBytes -> new DefaultDavProperty<Long>(name, numBytes))
                    .orElse(null);
        } else {
            return super.getProperty(name);
        }
    }

}