
org.modeshape.webdav.methods.DoLock Maven / Gradle / Ivy
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.webdav.methods;
import java.io.IOException;
import java.util.HashMap;
import java.util.Hashtable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import org.modeshape.common.i18n.TextI18n;
import org.modeshape.webdav.ITransaction;
import org.modeshape.webdav.IWebdavStore;
import org.modeshape.webdav.StoredObject;
import org.modeshape.webdav.WebdavStatus;
import org.modeshape.webdav.exceptions.LockFailedException;
import org.modeshape.webdav.exceptions.WebdavException;
import org.modeshape.webdav.fromcatalina.XMLWriter;
import org.modeshape.webdav.locking.IResourceLocks;
import org.modeshape.webdav.locking.LockedObject;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class DoLock extends AbstractMethod {
private final IWebdavStore store;
private final IResourceLocks resourceLocks;
private final boolean readOnly;
private boolean macLockRequest = false;
private boolean exclusive = false;
private String type = null;
private String lockOwner = null;
private String path = null;
private String parentPath = null;
private String userAgent = null;
public DoLock( IWebdavStore store,
IResourceLocks resourceLocks,
boolean readOnly ) {
this.store = store;
this.resourceLocks = resourceLocks;
this.readOnly = readOnly;
}
@Override
public void execute( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws IOException, LockFailedException {
logger.trace("-- " + this.getClass().getName());
if (readOnly) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
} else {
path = getRelativePath(req);
parentPath = getParentPath(getCleanPath(path));
if (!isUnlocked(transaction, req, resourceLocks, path)) {
resp.setStatus(WebdavStatus.SC_LOCKED);
return; // resource is locked
}
if (!isUnlocked(transaction, req, resourceLocks, parentPath)) {
resp.setStatus(WebdavStatus.SC_LOCKED);
return; // parent is locked
}
// Mac OS Finder (whether 10.4.x or 10.5) can't store files
// because executing a LOCK without lock information causes a
// SC_BAD_REQUEST
userAgent = req.getHeader("User-Agent");
if (userAgent != null && userAgent.contains("Darwin")) {
macLockRequest = true;
String timeString = Long.toString(System.currentTimeMillis());
lockOwner = userAgent.concat(timeString);
}
String tempLockOwner = "doLock" + System.currentTimeMillis() + req.toString();
if (resourceLocks.lock(transaction, path, tempLockOwner, false, 0, TEMP_TIMEOUT, TEMPORARY)) {
try {
if (req.getHeader("If") != null) {
doRefreshLock(transaction, req, resp);
} else {
doLock(transaction, req, resp);
}
} catch (LockFailedException e) {
resp.sendError(WebdavStatus.SC_LOCKED);
logger.error(e, new TextI18n("Lockfailed exception"));
} finally {
resourceLocks.unlockTemporaryLockedObjects(transaction, path, tempLockOwner);
}
}
}
}
private void doLock( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws IOException, LockFailedException {
StoredObject so = store.getStoredObject(transaction, path);
if (so != null) {
doLocking(transaction, req, resp);
} else {
// resource doesn't exist, null-resource lock
doNullResourceLock(transaction, req, resp);
}
so = null;
exclusive = false;
type = null;
lockOwner = null;
}
private void doLocking( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws IOException {
// Tests if LockObject on requested path exists, and if so, tests
// exclusivity
LockedObject lo = resourceLocks.getLockedObjectByPath(transaction, path);
if (lo != null) {
if (lo.isExclusive()) {
sendLockFailError(req, resp);
return;
}
}
try {
// Thats the locking itself
executeLock(transaction, req, resp);
} catch (ServletException e) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
logger.trace(e.toString());
} catch (LockFailedException e) {
sendLockFailError(req, resp);
} finally {
lo = null;
}
}
private void doNullResourceLock( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws IOException {
StoredObject parentSo, nullSo = null;
try {
parentSo = store.getStoredObject(transaction, parentPath);
if (parentPath != null && parentSo == null) {
store.createFolder(transaction, parentPath);
} else if (parentPath != null && parentSo != null && parentSo.isResource()) {
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
return;
}
nullSo = store.getStoredObject(transaction, path);
if (nullSo == null) {
// resource doesn't exist
store.createResource(transaction, path);
// Transmit expects 204 response-code, not 201
if (userAgent != null && userAgent.contains("Transmit")) {
logger.trace("DoLock.execute() : do workaround for user agent '" + userAgent + "'");
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
} else {
resp.setStatus(WebdavStatus.SC_CREATED);
}
} else {
// resource already exists, could not execute null-resource lock
sendLockFailError(req, resp);
return;
}
nullSo = store.getStoredObject(transaction, path);
if (nullSo == null) {
resp.setStatus(WebdavStatus.SC_NOT_FOUND);
} else {
// define the newly created resource as null-resource
nullSo.setNullResource(true);
// Thats the locking itself
executeLock(transaction, req, resp);
}
} catch (LockFailedException e) {
sendLockFailError(req, resp);
} catch (WebdavException e) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
logger.error(e, new TextI18n("Webdav exception"));
} catch (ServletException e) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
logger.error(e, new TextI18n("Servlet exception"));
} finally {
parentSo = null;
nullSo = null;
}
}
private void doRefreshLock( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws IOException, LockFailedException {
String[] lockTokens = getLockIdFromIfHeader(req);
String lockToken = null;
if (lockTokens != null) {
lockToken = lockTokens[0];
}
if (lockToken != null) {
// Getting LockObject of specified lockToken in If header
LockedObject refreshLo = resourceLocks.getLockedObjectByID(transaction, lockToken);
if (refreshLo != null) {
int timeout = getTimeout(req);
refreshLo.refreshTimeout(timeout);
// sending success response
generateXMLReport(resp, refreshLo);
refreshLo = null;
} else {
// no LockObject to given lockToken
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
}
} else {
resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
}
}
// ------------------------------------------------- helper methods
private void executeLock( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws LockFailedException, IOException, ServletException {
// Mac OS lock request workaround
if (macLockRequest) {
logger.trace("DoLock.execute() : do workaround for user agent '" + userAgent + "'");
doMacLockRequestWorkaround(transaction, req, resp);
} else {
// Getting LockInformation from request
if (getLockInformation(req, resp)) {
int depth = getDepth(req);
int lockDuration = getTimeout(req);
boolean lockSuccess = false;
if (exclusive) {
lockSuccess = resourceLocks.exclusiveLock(transaction, path, lockOwner, depth, lockDuration);
} else {
lockSuccess = resourceLocks.sharedLock(transaction, path, lockOwner, depth, lockDuration);
}
if (lockSuccess) {
// Locks successfully placed - return information about
LockedObject lo = resourceLocks.getLockedObjectByPath(transaction, path);
if (lo != null) {
generateXMLReport(resp, lo);
} else {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
}
} else {
sendLockFailError(req, resp);
throw new LockFailedException();
}
} else {
// information for LOCK could not be read successfully
resp.setContentType("text/xml; charset=UTF-8");
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
}
}
}
private boolean getLockInformation( HttpServletRequest req,
HttpServletResponse resp ) throws ServletException, IOException {
Node lockInfoNode = null;
DocumentBuilder documentBuilder = null;
documentBuilder = getDocumentBuilder();
try {
Document document = documentBuilder.parse(new InputSource(req.getInputStream()));
// Get the root element of the document
lockInfoNode = document.getDocumentElement();
if (lockInfoNode != null) {
NodeList childList = lockInfoNode.getChildNodes();
Node lockScopeNode = null;
Node lockTypeNode = null;
Node lockOwnerNode = null;
Node currentNode = null;
String nodeName = null;
for (int i = 0; i < childList.getLength(); i++) {
currentNode = childList.item(i);
if (currentNode.getNodeType() == Node.ELEMENT_NODE || currentNode.getNodeType() == Node.TEXT_NODE) {
nodeName = currentNode.getNodeName();
if (nodeName.endsWith("locktype")) {
lockTypeNode = currentNode;
}
if (nodeName.endsWith("lockscope")) {
lockScopeNode = currentNode;
}
if (nodeName.endsWith("owner")) {
lockOwnerNode = currentNode;
}
} else {
return false;
}
}
if (lockScopeNode != null) {
String scope = null;
childList = lockScopeNode.getChildNodes();
for (int i = 0; i < childList.getLength(); i++) {
currentNode = childList.item(i);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
scope = currentNode.getNodeName();
if (scope.endsWith("exclusive")) {
exclusive = true;
} else if (scope.equals("shared")) {
exclusive = false;
}
}
}
if (scope == null) {
return false;
}
} else {
return false;
}
if (lockTypeNode != null) {
childList = lockTypeNode.getChildNodes();
for (int i = 0; i < childList.getLength(); i++) {
currentNode = childList.item(i);
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
type = currentNode.getNodeName();
if (type.endsWith("write")) {
type = "write";
} else if (type.equals("read")) {
type = "read";
}
}
}
if (type == null) {
return false;
}
} else {
return false;
}
if (lockOwnerNode != null) {
childList = lockOwnerNode.getChildNodes();
for (int i = 0; i < childList.getLength(); i++) {
currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.ELEMENT_NODE:
lockOwner = currentNode.getFirstChild().getNodeValue();
break;
case Node.TEXT_NODE:
lockOwner = currentNode.getNodeValue();
break;
}
}
}
if (lockOwner == null) {
return false;
}
} else {
return false;
}
} catch (DOMException e) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
logger.error(e, new TextI18n("DOM exception"));
return false;
} catch (SAXException e) {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
logger.error(e, new TextI18n("SAX exception"));
return false;
}
return true;
}
private int getTimeout( HttpServletRequest req ) {
int lockDuration = DEFAULT_TIMEOUT;
String lockDurationStr = req.getHeader("Timeout");
if (lockDurationStr == null) {
lockDuration = DEFAULT_TIMEOUT;
} else {
int commaPos = lockDurationStr.indexOf(',');
// if multiple timeouts, just use the first one
if (commaPos != -1) {
lockDurationStr = lockDurationStr.substring(0, commaPos);
}
if (lockDurationStr.startsWith("Second-")) {
lockDuration = Integer.valueOf(lockDurationStr.substring(7));
} else {
if (lockDurationStr.equalsIgnoreCase("infinity")) {
lockDuration = MAX_TIMEOUT;
} else {
try {
lockDuration = Integer.valueOf(lockDurationStr);
} catch (NumberFormatException e) {
lockDuration = MAX_TIMEOUT;
}
}
}
if (lockDuration <= 0) {
lockDuration = DEFAULT_TIMEOUT;
}
if (lockDuration > MAX_TIMEOUT) {
lockDuration = MAX_TIMEOUT;
}
}
return lockDuration;
}
private void generateXMLReport( HttpServletResponse resp,
LockedObject lockedObject ) throws IOException {
HashMap namespaces = new HashMap();
namespaces.put("DAV:", "D");
resp.setStatus(WebdavStatus.SC_OK);
resp.setContentType("text/xml; charset=UTF-8");
XMLWriter generatedXML = new XMLWriter(resp.getWriter(), namespaces);
generatedXML.writeXMLHeader();
generatedXML.writeElement("DAV::prop", XMLWriter.OPENING);
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.OPENING);
generatedXML.writeElement("DAV::activelock", XMLWriter.OPENING);
generatedXML.writeElement("DAV::locktype", XMLWriter.OPENING);
generatedXML.writeProperty("DAV::" + type);
generatedXML.writeElement("DAV::locktype", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::lockscope", XMLWriter.OPENING);
if (exclusive) {
generatedXML.writeProperty("DAV::exclusive");
} else {
generatedXML.writeProperty("DAV::shared");
}
generatedXML.writeElement("DAV::lockscope", XMLWriter.CLOSING);
int depth = lockedObject.getLockDepth();
generatedXML.writeElement("DAV::depth", XMLWriter.OPENING);
if (depth == INFINITY) {
generatedXML.writeText("Infinity");
} else {
generatedXML.writeText(String.valueOf(depth));
}
generatedXML.writeElement("DAV::depth", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::owner", XMLWriter.OPENING);
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
generatedXML.writeText(lockOwner);
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::owner", XMLWriter.CLOSING);
long timeout = lockedObject.getTimeoutMillis();
generatedXML.writeElement("DAV::timeout", XMLWriter.OPENING);
generatedXML.writeText("Second-" + timeout / 1000);
generatedXML.writeElement("DAV::timeout", XMLWriter.CLOSING);
String lockToken = lockedObject.getID();
generatedXML.writeElement("DAV::locktoken", XMLWriter.OPENING);
generatedXML.writeElement("DAV::href", XMLWriter.OPENING);
generatedXML.writeText("opaquelocktoken:" + lockToken);
generatedXML.writeElement("DAV::href", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::locktoken", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::activelock", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::lockdiscovery", XMLWriter.CLOSING);
generatedXML.writeElement("DAV::prop", XMLWriter.CLOSING);
resp.addHeader("Lock-Token", "");
generatedXML.sendData();
}
private void doMacLockRequestWorkaround( ITransaction transaction,
HttpServletRequest req,
HttpServletResponse resp ) throws LockFailedException, IOException {
LockedObject lo;
int depth = getDepth(req);
int lockDuration = getTimeout(req);
if (lockDuration < 0 || lockDuration > MAX_TIMEOUT) {
lockDuration = DEFAULT_TIMEOUT;
}
boolean lockSuccess = false;
lockSuccess = resourceLocks.exclusiveLock(transaction, path, lockOwner, depth, lockDuration);
if (lockSuccess) {
// Locks successfully placed - return information about
lo = resourceLocks.getLockedObjectByPath(transaction, path);
if (lo != null) {
generateXMLReport(resp, lo);
} else {
resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
}
} else {
// Locking was not successful
sendLockFailError(req, resp);
}
}
private void sendLockFailError( HttpServletRequest req,
HttpServletResponse resp ) throws IOException {
Hashtable errorList = new Hashtable();
errorList.put(path, WebdavStatus.SC_LOCKED);
sendReport(req, resp, errorList);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy