mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-22 12:41:27 +00:00
now passing 94.6% of litmus lock tests
This commit is contained in:
@@ -14,7 +14,7 @@ import org.apache.jackrabbit.webdav.DavLocatorFactory;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.delegating.DelegatingFile;
|
||||
|
||||
public class FileLocator extends DelegatingFile<FolderLocator>implements FileSystemResourceLocator {
|
||||
public class FileLocator extends DelegatingFile<FolderLocator>implements InternalFileSystemResourceLocator {
|
||||
|
||||
private final DavLocatorFactory factory;
|
||||
private final String prefix;
|
||||
|
||||
@@ -9,24 +9,13 @@
|
||||
package org.cryptomator.filesystem.jackrabbit;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.jackrabbit.webdav.DavResourceLocator;
|
||||
import org.apache.jackrabbit.webdav.util.EncodeUtil;
|
||||
import org.cryptomator.common.LazyInitializer;
|
||||
import org.cryptomator.filesystem.Node;
|
||||
|
||||
public interface FileSystemResourceLocator extends DavResourceLocator, Node {
|
||||
|
||||
@Override
|
||||
default String getResourcePath() {
|
||||
return LazyInitializer.initializeLazily(getResourcePathRef(), this::computeResourcePath);
|
||||
}
|
||||
|
||||
AtomicReference<String> getResourcePathRef();
|
||||
|
||||
String computeResourcePath();
|
||||
|
||||
@Override
|
||||
Optional<FolderLocator> parent();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.delegating.DelegatingFolder;
|
||||
|
||||
public class FolderLocator extends DelegatingFolder<FolderLocator, FileLocator>implements FileSystemResourceLocator {
|
||||
public class FolderLocator extends DelegatingFolder<FolderLocator, FileLocator>implements InternalFileSystemResourceLocator {
|
||||
|
||||
private final DavLocatorFactory factory;
|
||||
private final String prefix;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.cryptomator.filesystem.jackrabbit;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.cryptomator.common.LazyInitializer;
|
||||
|
||||
/**
|
||||
* Adds package-private API to {@link FileSystemResourceLocator}.
|
||||
*/
|
||||
interface InternalFileSystemResourceLocator extends FileSystemResourceLocator {
|
||||
|
||||
@Override
|
||||
default String getResourcePath() {
|
||||
return LazyInitializer.initializeLazily(getResourcePathRef(), this::computeResourcePath);
|
||||
}
|
||||
|
||||
AtomicReference<String> getResourcePathRef();
|
||||
|
||||
String computeResourcePath();
|
||||
|
||||
}
|
||||
@@ -16,12 +16,12 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
import org.apache.jackrabbit.webdav.DavResourceLocator;
|
||||
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;
|
||||
@@ -78,7 +78,7 @@ abstract class DavNode<T extends FileSystemResourceLocator> implements DavResour
|
||||
}
|
||||
|
||||
@Override
|
||||
public DavResourceLocator getLocator() {
|
||||
public FileSystemResourceLocator getLocator() {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ abstract class DavNode<T extends FileSystemResourceLocator> implements DavResour
|
||||
|
||||
@Override
|
||||
public boolean isLockable(Type type, Scope scope) {
|
||||
return true;
|
||||
return Type.WRITE.equals(type) && Scope.EXCLUSIVE.equals(scope) || Scope.SHARED.equals(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,18 +218,12 @@ abstract class DavNode<T extends FileSystemResourceLocator> implements DavResour
|
||||
@Override
|
||||
public ActiveLock[] getLocks() {
|
||||
final ActiveLock exclusiveWriteLock = getLock(Type.WRITE, Scope.EXCLUSIVE);
|
||||
if (exclusiveWriteLock != null) {
|
||||
return new ActiveLock[] {exclusiveWriteLock};
|
||||
} else {
|
||||
return new ActiveLock[0];
|
||||
}
|
||||
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 {
|
||||
if (Scope.SHARED.equals(reqLockInfo.getScope())) {
|
||||
throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED, "Only exclusive write locks supported.");
|
||||
}
|
||||
return lockManager.createLock(reqLockInfo, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package org.cryptomator.webdav.jackrabbitservlet;
|
||||
|
||||
import org.apache.jackrabbit.webdav.DavConstants;
|
||||
import org.apache.jackrabbit.webdav.lock.AbstractActiveLock;
|
||||
import org.apache.jackrabbit.webdav.lock.LockInfo;
|
||||
import org.apache.jackrabbit.webdav.lock.Scope;
|
||||
import org.apache.jackrabbit.webdav.lock.Type;
|
||||
|
||||
public class ExclusiveSharedLock extends AbstractActiveLock {
|
||||
|
||||
private final String token;
|
||||
private final Type type;
|
||||
private final Scope scope;
|
||||
private String owner;
|
||||
private boolean isDeep = true; // deep by default
|
||||
private long expirationTime = DavConstants.INFINITE_TIMEOUT; // never expires by default;
|
||||
|
||||
ExclusiveSharedLock(String token, LockInfo lockInfo) {
|
||||
this.token = token;
|
||||
this.type = lockInfo.getType();
|
||||
this.scope = lockInfo.getScope();
|
||||
this.owner = lockInfo.getOwner();
|
||||
this.isDeep = lockInfo.isDeep();
|
||||
setTimeout(lockInfo.getTimeout());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLockedByToken(String lockToken) {
|
||||
return token.equals(lockToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
return System.currentTimeMillis() > expirationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout() {
|
||||
return expirationTime - System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimeout(long timeout) {
|
||||
if (timeout > 0) {
|
||||
expirationTime = System.currentTimeMillis() + timeout;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeep() {
|
||||
return isDeep;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsDeep(boolean isDeep) {
|
||||
this.isDeep = isDeep;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scope getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
/* HASHCODE / EQUALS */
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getToken().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ExclusiveSharedLock) {
|
||||
ExclusiveSharedLock other = (ExclusiveSharedLock) obj;
|
||||
return this.getToken().equals(other.getToken());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package org.cryptomator.webdav.jackrabbitservlet;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
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.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.cryptomator.filesystem.jackrabbit.FileSystemResourceLocator;
|
||||
import org.cryptomator.filesystem.jackrabbit.FolderLocator;
|
||||
|
||||
public class ExclusiveSharedLockManager implements LockManager {
|
||||
|
||||
private final ConcurrentMap<FileSystemResourceLocator, Map<String, ActiveLock>> lockedResources = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public ActiveLock createLock(LockInfo lockInfo, DavResource resource) throws DavException {
|
||||
Objects.requireNonNull(lockInfo);
|
||||
Objects.requireNonNull(resource);
|
||||
if (resource instanceof DavNode) {
|
||||
return createLockInternal(lockInfo, (DavNode<?>) resource);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource type " + resource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized ActiveLock createLockInternal(LockInfo lockInfo, DavNode<?> resource) throws DavException {
|
||||
FileSystemResourceLocator locator = resource.getLocator();
|
||||
removedExpiredLocksInLocatorHierarchy(locator);
|
||||
|
||||
ActiveLock existingExclusiveLock = getLock(lockInfo.getType(), Scope.EXCLUSIVE, resource);
|
||||
ActiveLock existingSharedLock = getLock(lockInfo.getType(), Scope.SHARED, resource);
|
||||
boolean hasExclusiveLock = existingExclusiveLock != null;
|
||||
boolean hasSharedLock = existingSharedLock != null;
|
||||
boolean isLocked = hasExclusiveLock || hasSharedLock;
|
||||
if ((Scope.EXCLUSIVE.equals(lockInfo.getScope()) && isLocked) || (Scope.SHARED.equals(lockInfo.getScope()) && hasExclusiveLock)) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "Resource already locked.");
|
||||
}
|
||||
|
||||
for (Entry<FileSystemResourceLocator, Map<String, ActiveLock>> entry : lockedResources.entrySet()) {
|
||||
final FileSystemResourceLocator entryLocator = entry.getKey();
|
||||
final Collection<ActiveLock> entryLocks = entry.getValue().values();
|
||||
if (isAncestor(entryLocator, locator) && isAffectedByParentLocks(lockInfo, locator, entryLocks, entryLocator)) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "Parent resource already locked. " + entryLocator);
|
||||
} else if (isAncestor(locator, entryLocator) && isAffectedByChildLocks(lockInfo, locator, entryLocks, entryLocator)) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Subresource already locked. " + entryLocator);
|
||||
}
|
||||
}
|
||||
|
||||
String token = DavConstants.OPAQUE_LOCK_TOKEN_PREFIX + UUID.randomUUID();
|
||||
return lockedResources.computeIfAbsent(locator, loc -> new HashMap<>()).computeIfAbsent(token, t -> new ExclusiveSharedLock(t, lockInfo));
|
||||
}
|
||||
|
||||
private void removedExpiredLocksInLocatorHierarchy(FileSystemResourceLocator locator) {
|
||||
lockedResources.getOrDefault(locator, Collections.emptyMap()).values().removeIf(ActiveLock::isExpired);
|
||||
locator.parent().ifPresent(this::removedExpiredLocksInLocatorHierarchy);
|
||||
}
|
||||
|
||||
private boolean isAncestor(FileSystemResourceLocator parent, FileSystemResourceLocator child) {
|
||||
if (parent instanceof FolderLocator) {
|
||||
FolderLocator folder = (FolderLocator) parent;
|
||||
return folder.isAncestorOf(child);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAffectedByParentLocks(LockInfo childLockInfo, FileSystemResourceLocator childLocator, Collection<ActiveLock> parentLocks, FileSystemResourceLocator parentLocator) {
|
||||
assert childLocator.parent().isPresent();
|
||||
for (ActiveLock lock : parentLocks) {
|
||||
if (Scope.SHARED.equals(childLockInfo.getScope()) && Scope.SHARED.equals(lock.getScope())) {
|
||||
continue;
|
||||
} else if (lock.isDeep() || childLocator.parent().get().equals(parentLocator)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isAffectedByChildLocks(LockInfo parentLockInfo, FileSystemResourceLocator parentLocator, Collection<ActiveLock> childLocks, FileSystemResourceLocator childLocator) {
|
||||
assert childLocator.parent().isPresent();
|
||||
for (ActiveLock lock : childLocks) {
|
||||
if (Scope.SHARED.equals(lock.getScope()) && Scope.SHARED.equals(parentLockInfo.getScope())) {
|
||||
continue;
|
||||
} else if (parentLockInfo.isDeep() || childLocator.parent().get().equals(parentLocator)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActiveLock refreshLock(LockInfo lockInfo, String lockToken, DavResource resource) throws DavException {
|
||||
ActiveLock lock = getLock(lockInfo.getType(), lockInfo.getScope(), resource);
|
||||
if (lock == null) {
|
||||
throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
|
||||
} else if (!lock.getToken().equals(lockToken)) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED);
|
||||
}
|
||||
lock.setTimeout(lockInfo.getTimeout());
|
||||
return lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void releaseLock(String lockToken, DavResource resource) throws DavException {
|
||||
if (resource instanceof DavNode) {
|
||||
try {
|
||||
releaseLockInternal(lockToken, (DavNode<?>) resource);
|
||||
} catch (UncheckedDavException e) {
|
||||
throw e.toDavException();
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource type " + resource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void releaseLockInternal(String lockToken, DavNode<?> resource) throws UncheckedDavException {
|
||||
lockedResources.compute(resource.getLocator(), (loc, locks) -> {
|
||||
if (locks == null || locks.isEmpty()) {
|
||||
// no lock exists, nothing needs to change.
|
||||
return null;
|
||||
} else if (!locks.containsKey(lockToken)) {
|
||||
throw new UncheckedDavException(DavServletResponse.SC_LOCKED, "Resource locked with different token.");
|
||||
} else {
|
||||
locks.remove(lockToken);
|
||||
return locks.isEmpty() ? null : locks;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActiveLock getLock(Type type, Scope scope, DavResource resource) {
|
||||
if (resource instanceof DavNode) {
|
||||
return getLockInternal(type, scope, ((DavNode<?>) resource).getLocator());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource type " + resource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private ActiveLock getLockInternal(Type type, Scope scope, FileSystemResourceLocator locator) {
|
||||
// try to find a lock directly on this resource:
|
||||
if (lockedResources.containsKey(locator)) {
|
||||
for (ActiveLock lock : lockedResources.get(locator).values()) {
|
||||
if (type.equals(lock.getType()) && scope.equals(lock.getScope())) {
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
}
|
||||
// or otherwise look for parent locks:
|
||||
if (locator.parent().isPresent()) {
|
||||
return getLockInternal(type, scope, locator.parent().get());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLock(String lockToken, DavResource resource) {
|
||||
if (resource instanceof DavNode) {
|
||||
return hasLockInternal(lockToken, (DavNode<?>) resource);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource type " + resource.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasLockInternal(String lockToken, DavNode<?> resource) {
|
||||
return lockedResources.getOrDefault(resource.getLocator(), Collections.emptyMap()).containsKey(lockToken);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import org.apache.jackrabbit.webdav.DavServletRequest;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
import org.apache.jackrabbit.webdav.DavSession;
|
||||
import org.apache.jackrabbit.webdav.lock.LockManager;
|
||||
import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
|
||||
import org.cryptomator.filesystem.jackrabbit.FileLocator;
|
||||
import org.cryptomator.filesystem.jackrabbit.FolderLocator;
|
||||
|
||||
@@ -25,7 +24,7 @@ class FilesystemResourceFactory implements DavResourceFactory {
|
||||
private final LockManager lockManager;
|
||||
|
||||
public FilesystemResourceFactory() {
|
||||
this.lockManager = new SimpleLockManager();
|
||||
this.lockManager = new ExclusiveSharedLockManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.cryptomator.webdav.jackrabbitservlet;
|
||||
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
|
||||
public class UncheckedDavException extends RuntimeException {
|
||||
|
||||
private final int errorCode;
|
||||
|
||||
public UncheckedDavException(int errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public UncheckedDavException(int errorCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.errorCode = errorCode;
|
||||
}
|
||||
|
||||
public DavException toDavException() {
|
||||
return new DavException(errorCode, getMessage(), getCause(), null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -78,42 +78,46 @@ public class WebDavServlet extends AbstractWebdavServlet {
|
||||
|
||||
@Override
|
||||
protected int validateDestination(DavResource destResource, WebdavRequest request, boolean checkHeader) throws DavException {
|
||||
if (destResource.hasLock(Type.WRITE, Scope.EXCLUSIVE) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(destResource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The destination resource was locked");
|
||||
if (isLocked(destResource) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(destResource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The destination resource is locked");
|
||||
}
|
||||
return super.validateDestination(destResource, request, checkHeader);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPut(WebdavRequest request, WebdavResponse response, DavResource resource) throws IOException, DavException {
|
||||
if (resource.hasLock(Type.WRITE, Scope.EXCLUSIVE) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource was locked");
|
||||
if (isLocked(resource) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource is locked");
|
||||
}
|
||||
super.doPut(request, response, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDelete(WebdavRequest request, WebdavResponse response, DavResource resource) throws IOException, DavException {
|
||||
if (resource.hasLock(Type.WRITE, Scope.EXCLUSIVE) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource was locked");
|
||||
if (isLocked(resource) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource is locked");
|
||||
}
|
||||
super.doDelete(request, response, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doMove(WebdavRequest request, WebdavResponse response, DavResource resource) throws IOException, DavException {
|
||||
if (resource.hasLock(Type.WRITE, Scope.EXCLUSIVE) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The source resource was locked");
|
||||
if (isLocked(resource) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The source resource is locked");
|
||||
}
|
||||
super.doMove(request, response, resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPropPatch(WebdavRequest request, WebdavResponse response, DavResource resource) throws IOException, DavException {
|
||||
if (resource.hasLock(Type.WRITE, Scope.EXCLUSIVE) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource was locked");
|
||||
if (isLocked(resource) && (request.getHeader(HEADER_IF) == null || !request.matchesIfHeader(resource))) {
|
||||
throw new DavException(DavServletResponse.SC_LOCKED, "The resource is locked");
|
||||
}
|
||||
super.doPropPatch(request, response, resource);
|
||||
}
|
||||
|
||||
private boolean isLocked(DavResource resource) {
|
||||
return resource.hasLock(Type.WRITE, Scope.EXCLUSIVE) || resource.hasLock(Type.WRITE, Scope.SHARED);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user