From 4b1ce34f510223c0ce594e20109e89363e7bcf48 Mon Sep 17 00:00:00 2001 From: jensp Date: Fri, 25 May 2018 14:38:15 +0000 Subject: [PATCH] CcmNG: Implementation of the lock method for WebDAV access to theme files git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@5450 8810af33-2d31-482b-a856-94f89814c4df --- .../webdav/AlreadyLockedException.java | 67 +++++++++++++ .../libreccm/theming/webdav/ThemeFiles.java | 55 +++++++++-- .../theming/webdav/ThemeFilesLockManager.java | 98 +++++++++++++++++++ 3 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 ccm-core/src/main/java/org/libreccm/theming/webdav/AlreadyLockedException.java create mode 100644 ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFilesLockManager.java diff --git a/ccm-core/src/main/java/org/libreccm/theming/webdav/AlreadyLockedException.java b/ccm-core/src/main/java/org/libreccm/theming/webdav/AlreadyLockedException.java new file mode 100644 index 000000000..310b94fe5 --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/webdav/AlreadyLockedException.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +package org.libreccm.theming.webdav; + +/** + * + * @author Jens Pelzetter + */ +class AlreadyLockedException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of AlreadyLockedException without detail message. + */ + public AlreadyLockedException() { + super(); + } + + + /** + * Constructs an instance of AlreadyLockedException with the specified detail message. + * + * @param msg The detail message. + */ + public AlreadyLockedException(final String msg) { + super(msg); + } + + /** + * Constructs an instance of AlreadyLockedException which wraps the + * specified exception. + * + * @param exception The exception to wrap. + */ + public AlreadyLockedException(final Exception exception) { + super(exception); + } + + /** + * Constructs an instance of AlreadyLockedException with the specified message which also wraps the + * specified exception. + * + * @param msg The detail message. + * @param exception The exception to wrap. + */ + public AlreadyLockedException(final String msg, final Exception exception) { + super(msg, exception); + } +} diff --git a/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java index 689c6d379..e0f5f5517 100644 --- a/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java +++ b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFiles.java @@ -41,16 +41,24 @@ import org.libreccm.theming.ThemeInfo; import org.libreccm.theming.ThemeVersion; import org.libreccm.theming.Themes; import org.libreccm.webdav.ResponseStatus; +import org.libreccm.webdav.methods.LOCK; +import org.libreccm.webdav.xml.elements.ActiveLock; import org.libreccm.webdav.xml.elements.Collection; +import org.libreccm.webdav.xml.elements.Depth; import org.libreccm.webdav.xml.elements.HRef; +import org.libreccm.webdav.xml.elements.LockInfo; +import org.libreccm.webdav.xml.elements.LockRoot; +import org.libreccm.webdav.xml.elements.LockToken; import org.libreccm.webdav.xml.elements.MultiStatus; import org.libreccm.webdav.xml.elements.Prop; import org.libreccm.webdav.xml.elements.PropStat; import org.libreccm.webdav.xml.elements.Status; +import org.libreccm.webdav.xml.elements.TimeOut; import org.libreccm.webdav.xml.elements.WebDavResponse; import org.libreccm.webdav.xml.properties.DisplayName; import org.libreccm.webdav.xml.properties.GetContentLength; import org.libreccm.webdav.xml.properties.GetContentType; +import org.libreccm.webdav.xml.properties.LockDiscovery; import java.util.LinkedList; import java.util.List; @@ -72,14 +80,17 @@ import javax.ws.rs.ext.Providers; public class ThemeFiles { @Inject - private Themes themes; + private HttpServletRequest request; @Inject private ServletContext servletContext; - + @Inject - private HttpServletRequest request; - + private Themes themes; + + @Inject + private ThemeFilesLockManager lockManager; + @GET @Path("/{path}") public Response getFile(@PathParam("theme") final String theme, @@ -117,6 +128,38 @@ public class ThemeFiles { } + @LOCK + @Path("/{path}") + public Prop lock(@PathParam("theme") + final String theme, + @PathParam("path") + final String path, + final LockInfo lockInfo) { + + final String lockedPath = String.format("%s/path", + theme, + path); + + try { + final String lockToken = lockManager.lockFile(lockedPath); + + return new Prop(new LockDiscovery( + new ActiveLock(lockInfo.getLockScope(), + lockInfo.getLockType(), + Depth.ZERO, + lockInfo.getOwner(), + new TimeOut(3600), + new LockToken(new HRef(String + .format("opaquelocktoken:%s", + lockToken))), + new LockRoot(new HRef(lockedPath))))); + + } catch (AlreadyLockedException ex) { + throw new WebApplicationException( + ResponseStatus.LOCKED.getStatusCode()); + } + } + @OPTIONS public Response options() { @@ -209,10 +252,10 @@ public class ThemeFiles { return response; } - + private WebDavResponse buildWebDavResponse(final String basePath, final ThemeFileInfo fileInfo) { - + final PropStat propStat; if (fileInfo.isDirectory()) { propStat = new PropStat( diff --git a/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFilesLockManager.java b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFilesLockManager.java new file mode 100644 index 000000000..c6f16c0ac --- /dev/null +++ b/ccm-core/src/main/java/org/libreccm/theming/webdav/ThemeFilesLockManager.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 LibreCCM Foundation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +package org.libreccm.theming.webdav; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import javax.enterprise.context.ApplicationScoped; + +/** + * Manages the locks on files for WebDAV. + * + * @author Jens Pelzetter + */ +@ApplicationScoped +class ThemeFilesLockManager { + + /** + * Mapping between file path and lock token + */ + private final Map lockedFiles = new HashMap<>(); + + /** + * Mapping between lock token and file. + */ + private final Map locks = new HashMap<>(); + + /** + * Lock a file + * + * @param file Path of the file to lock. + * + * @return The lock token. + * + * @throws AlreadyLockedException If the file is already locked. + */ + protected String lockFile(final String file) throws AlreadyLockedException { + + if (lockedFiles.containsKey(file)) { + throw new AlreadyLockedException(String.format( + "File %s is already locked.", file)); + } else { + + final String lockToken = UUID.randomUUID().toString(); + lockedFiles.put(file, lockToken); + locks.put(lockToken, file); + + return lockToken; + } + } + + /** + * Check if a file is locked. + * + * @param file The file to check for a lock. + * + * @return An {@link Optional} with the lock token of the file if the file + * is locked, an empty {@code Optional} otherwise. + */ + protected Optional isLocked(final String file) { + if (lockedFiles.containsKey(file)) { + return Optional.of(lockedFiles.get(file)); + } else { + return Optional.empty(); + } + } + + /** + * Removes the lock from a file. + * + * @param lockToken The token of the lock to remove. + */ + protected void unlock(final String lockToken) { + + final String file = locks.get(lockToken); + locks.remove(lockToken); + lockedFiles.remove(file); + } + +}