org.cryptomator.frontend.webdav.servlet.DavNode.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptomator.frontend.webdav.servlet.DavNode.java

Source

/*******************************************************************************
 * Copyright (c) 2016 Sebastian Stenzel and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the accompanying LICENSE.txt.
 *
 * Contributors:
 *     Sebastian Stenzel - initial API and implementation
 *******************************************************************************/
package org.cryptomator.frontend.webdav.servlet;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.DavResource;
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.lock.ActiveLock;
import org.apache.jackrabbit.webdav.lock.LockInfo;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.Scope;
import org.apache.jackrabbit.webdav.lock.Type;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.apache.jackrabbit.webdav.property.PropEntry;

abstract class DavNode implements DavResource {

    private static final String DAV_COMPLIANCE_CLASSES = "1, 2";
    private static final String[] DAV_CREATIONDATE_PROPNAMES = { DavConstants.PROPERTY_CREATIONDATE,
            "Win32CreationTime" };
    private static final String[] DAV_MODIFIEDDATE_PROPNAMES = { DavConstants.PROPERTY_GETLASTMODIFIED,
            "Win32LastModifiedTime" };

    protected final DavResourceFactoryImpl factory;
    protected final LockManager lockManager;
    protected final DavLocatorImpl locator;
    protected final Path path;
    protected final Optional<BasicFileAttributes> attr;
    protected final DavSession session;
    protected final DavPropertySet properties;

    public DavNode(DavResourceFactoryImpl factory, LockManager lockManager, DavLocatorImpl locator, Path path,
            Optional<BasicFileAttributes> attr, DavSession session) {
        this.factory = factory;
        this.lockManager = lockManager;
        this.locator = locator;
        this.path = path;
        this.attr = attr;
        this.session = session;
        this.properties = new DavPropertySet();
    }

    @Override
    public String getComplianceClass() {
        return DAV_COMPLIANCE_CLASSES;
    }

    @Override
    public String getSupportedMethods() {
        return METHODS;
    }

    @Override
    public boolean exists() {
        return attr.isPresent();
    }

    @Override
    public String getDisplayName() {
        String[] pathElements = StringUtils.split(getResourcePath(), '/');
        return pathElements[pathElements.length - 1];
    }

    @Override
    public DavLocatorImpl getLocator() {
        return locator;
    }

    @Override
    public String getResourcePath() {
        return locator.getResourcePath();
    }

    @Override
    public String getHref() {
        return locator.getHref(isCollection());
    }

    @Override
    public long getModificationTime() {
        return attr.map(BasicFileAttributes::lastModifiedTime).map(FileTime::toInstant).map(Instant::toEpochMilli)
                .orElse(-1l);
    }

    protected void setModificationTime(Instant instant) throws DavException {
        BasicFileAttributeView attrView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
        if (attrView != null) {
            try {
                attrView.setTimes(FileTime.from(instant), null, null);
            } catch (IOException e) {
                throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            }
        }
    }

    protected void setCreationTime(Instant instant) throws DavException {
        BasicFileAttributeView attrView = Files.getFileAttributeView(path, BasicFileAttributeView.class);
        if (attrView != null) {
            try {
                attrView.setTimes(null, null, FileTime.from(instant));
            } catch (IOException e) {
                throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            }
        }
    }

    @Override
    public DavPropertyName[] getPropertyNames() {
        return getProperties().getPropertyNames();
    }

    @Override
    public DavProperty<?> getProperty(DavPropertyName name) {
        final String namespacelessPropertyName = name.getName();
        if (Arrays.asList(DAV_CREATIONDATE_PROPNAMES).contains(namespacelessPropertyName)) {
            return creationDateProperty(name).orElse(null);
        } else if (Arrays.asList(DAV_MODIFIEDDATE_PROPNAMES).contains(namespacelessPropertyName)) {
            return lastModifiedDateProperty(name).orElse(null);
        } else {
            return properties.get(name);
        }
    }

    /**
     * Returns a current snapshot of all available properties.
     */
    @Override
    public DavPropertySet getProperties() {
        creationDateProperty(DavPropertyName.CREATIONDATE).ifPresent(properties::add);
        lastModifiedDateProperty(DavPropertyName.GETLASTMODIFIED).ifPresent(properties::add);
        return properties;
    }

    private Optional<DavProperty<?>> lastModifiedDateProperty(DavPropertyName name) {
        return attr.map(BasicFileAttributes::lastModifiedTime) //
                .map(FileTime::toInstant) //
                .map(creationTime -> OffsetDateTime.ofInstant(creationTime, ZoneOffset.UTC)) //
                .map(creationDate -> new DefaultDavProperty<>(name,
                        DateTimeFormatter.RFC_1123_DATE_TIME.format(creationDate)));
    }

    private Optional<DavProperty<?>> creationDateProperty(DavPropertyName name) {
        return attr.map(BasicFileAttributes::creationTime) //
                .map(FileTime::toInstant) //
                .map(creationTime -> OffsetDateTime.ofInstant(creationTime, ZoneOffset.UTC)) //
                .map(creationDate -> new DefaultDavProperty<>(name,
                        DateTimeFormatter.RFC_1123_DATE_TIME.format(creationDate)));
    }

    @Override
    public void setProperty(DavProperty<?> property) throws DavException {
        final String namespacelessPropertyName = property.getName().getName();
        if (Arrays.asList(DAV_CREATIONDATE_PROPNAMES).contains(namespacelessPropertyName)
                && property.getValue() instanceof String) {
            String createDateStr = (String) property.getValue();
            OffsetDateTime creationDate = OffsetDateTime
                    .from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(createDateStr));
            this.setCreationTime(creationDate.toInstant());
        } else if (Arrays.asList(DAV_MODIFIEDDATE_PROPNAMES).contains(namespacelessPropertyName)
                && property.getValue() instanceof String) {
            String lastModifiedDateStr = (String) property.getValue();
            OffsetDateTime lastModifiedDate = OffsetDateTime
                    .from(DateTimeFormatter.RFC_1123_DATE_TIME.parse(lastModifiedDateStr));
            this.setModificationTime(lastModifiedDate.toInstant());
        }
        properties.add(property);
    }

    @Override
    public void removeProperty(DavPropertyName propertyName) throws DavException {
        getProperties().remove(propertyName);
    }

    @Override
    public MultiStatusResponse alterProperties(List<? extends PropEntry> changeList) throws DavException {
        final DavPropertyNameSet names = new DavPropertyNameSet();
        for (final PropEntry entry : changeList) {
            if (entry instanceof DavProperty) {
                final DavProperty<?> prop = (DavProperty<?>) entry;
                this.setProperty(prop);
                names.add(prop.getName());
            } else if (entry instanceof DavPropertyName) {
                final DavPropertyName name = (DavPropertyName) entry;
                this.removeProperty(name);
                names.add(name);
            }
        }
        return new MultiStatusResponse(this, names);
    }

    @Override
    public DavFolder getCollection() {
        DavLocatorImpl parentLocator = locator.resolveParent();
        if (parentLocator == null) {
            return null;
        } else {
            Path parentPath = path.getParent();
            BasicFileAttributes parentAttr;
            try {
                parentAttr = Files.readAttributes(parentPath, BasicFileAttributes.class);
            } catch (NoSuchFileException e) {
                parentAttr = null;
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            return factory.createFolder(parentLocator, parentPath, Optional.ofNullable(parentAttr), session);
        }
    }

    @Override
    public boolean isLockable(Type type, Scope scope) {
        return Type.WRITE.equals(type) && Scope.EXCLUSIVE.equals(scope) || Scope.SHARED.equals(scope);
    }

    @Override
    public boolean hasLock(Type type, Scope scope) {
        return getLock(type, scope) != null;
    }

    @Override
    public ActiveLock getLock(Type type, Scope scope) {
        return lockManager.getLock(type, scope, this);
    }

    @Override
    public ActiveLock[] getLocks() {
        final ActiveLock exclusiveWriteLock = getLock(Type.WRITE, Scope.EXCLUSIVE);
        final ActiveLock sharedWriteLock = getLock(Type.WRITE, Scope.SHARED);
        return Stream.of(exclusiveWriteLock, sharedWriteLock).filter(Objects::nonNull).toArray(ActiveLock[]::new);
    }

    @Override
    public ActiveLock lock(LockInfo reqLockInfo) throws DavException {
        return lockManager.createLock(reqLockInfo, this);
    }

    @Override
    public ActiveLock refreshLock(LockInfo reqLockInfo, String lockToken) throws DavException {
        return lockManager.refreshLock(reqLockInfo, lockToken, this);
    }

    @Override
    public void unlock(String lockToken) throws DavException {
        lockManager.releaseLock(lockToken, this);
    }

    @Override
    public void addLockManager(LockManager lockmgr) {
        throw new UnsupportedOperationException("Locks are managed");
    }

    @Override
    public DavResourceFactoryImpl getFactory() {
        return factory;
    }

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

}