/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/LockMethod.java,v 1.55.2.3 2004/03/23 13:32:08 ozeigermann Exp $
* $Revision: 1.55.2.3 $
* $Date: 2004/03/23 13:32:08 $
*
* ====================================================================
*
* Copyright 1999-2002 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.apache.slide.webdav.method;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.NamespaceConfig;
import org.apache.slide.common.SlideException;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.lock.NodeLock;
import org.apache.slide.lock.ObjectIsAlreadyLockedException;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.XMLValue;
import org.apache.slide.webdav.WebdavException;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.util.PropertyHelper;
import org.apache.slide.webdav.util.WebdavConstants;
import org.apache.util.WebdavStatus;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.output.XMLOutputter;
/**
* LOCK method.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
*/
public class LockMethod extends AbstractMultistatusResponseMethod
implements WebdavConstants {
// -------------------------------------------------------------- Constants
/**
* Create a new lock.
*/
private static final int LOCK_CREATION = 0;
/**
* Refresh lock.
*/
private static final int LOCK_REFRESH = 1;
/**
* Maximum and default timeout.
*/
private static final int MAX_TIMEOUT = Integer.MAX_VALUE;
private static final int DEFAULT_TIMEOUT = MAX_TIMEOUT;
/**
* The default owner if not explicitely specified by the request.
*/
public static final String DEFAULT_LOCK_OWNER = "";
// ----------------------------------------------------- Instance Variables
/**
* Depth.
*/
private int depth;
/**
* Type of the LOCK method.
*/
private int lockType;
/**
* Lock duration.
*/
private int lockDuration = DEFAULT_TIMEOUT;
/**
* DAV Namespace support.
*/
private boolean davNative;
/**
* Lock scope.
*/
private String lockInfo_lockScope;
/**
* Lock type.
*/
private String lockInfo_lockType;
/**
* Lock owner.
*/
private String lockInfo_lockOwner;
/**
* Lock subject.
*/
private String lockInfo_lockSubject;
/**
* The PropertyHelper used by this instance.
*/
protected PropertyHelper propertyHelper = null;
// ----------------------------------------------------------- Constructors
/**
* Constructor.
*
* @param token the token for accessing the namespace
* @param config configuration of the WebDAV servlet
*/
public LockMethod(NamespaceAccessToken token, WebdavServletConfig config) {
super(token, config);
}
// ------------------------------------------------------ Protected Methods
/**
* Parse request.
*
* @exception WebdavException Does not happen
*/
protected void parseRequest()
throws WebdavException {
propertyHelper = PropertyHelper.getPropertyHelper(slideToken, token, getConfig());
// readRequestContent();
// Loads the associated object from the store.
lockInfo_lockSubject = requestUri;
if (lockInfo_lockSubject == null) {
lockInfo_lockSubject = "/";
}
depth = requestHeaders.getDepth(INFINITY);
if (depth != 0 && depth != INFINITY) {
int sc = WebdavStatus.SC_PRECONDITION_FAILED;
sendError( sc, "Invalid header Depth: "+depth );
throw new WebdavException( sc );
}
lockDuration = requestHeaders.getTimeout(MAX_TIMEOUT);
if (req.getContentLength() > 0) {
parseLockInfo();
}
else {
lockType = LOCK_REFRESH;
}
}
/**
* Parses the <code><lockinfo></code> request content document.
*
* @throws WebdavException if parsing the request failed or if
* the request is not valid.
*/
private void parseLockInfo() throws WebdavException {
lockType = LOCK_CREATION;
try {
Element lockScopeElement = null;
Element lockTypeElement = null;
Element lockOwnerElement = null;
Iterator childrenIterator = parseRequestContent(E_LOCKINFO).getChildren().iterator();
Element currentElement = null;
while (childrenIterator.hasNext()) {
currentElement = (Element)childrenIterator.next();
if (E_LOCKSCOPE.equals(currentElement.getName())) {
lockScopeElement = currentElement;
}
else if (E_LOCKTYPE.equals(currentElement.getName())) {
lockTypeElement = currentElement;
}
else if (E_OWNER.equals(currentElement.getName())) {
lockOwnerElement = currentElement;
}
}
parseLockScope(lockScopeElement);
parseLockType(lockTypeElement);
parseOwner(lockOwnerElement);
}
catch (JDOMException e) {
int statusCode = WebdavStatus.SC_BAD_REQUEST;
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
catch (IOException e) {
int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
}
/**
* Parses the <code><lockscope></code> part of the request content
* document.
*
* @param lockScopeElement the <code><lockscope></code> to parse.
*
* @throws JDOMException if parsing the request failed or if
* the request is not valid.
*/
private void parseLockScope(Element lockScopeElement) throws JDOMException {
if (lockScopeElement == null) {
throw new JDOMException("Expected <"+E_LOCKSCOPE+"> element");
}
List children = lockScopeElement.getChildren();
if (children.size() != 1) {
throw new JDOMException("<"+E_LOCKSCOPE+"> must have exactly one child element");
}
lockInfo_lockScope = ((Element)children.get(0)).getName();
}
/**
* Parses the <code><locktype></code> part of the request content
* document.
*
* @param lockTypeElement the <code><locktype></code> to parse.
*
* @throws JDOMException if parsing the request failed or if
* the request is not valid.
*/
private void parseLockType(Element lockTypeElement) throws JDOMException {
if (lockTypeElement == null) {
throw new JDOMException("Expected <"+E_LOCKTYPE+"> element");
}
List children = lockTypeElement.getChildren();
if (children.size() != 1) {
throw new JDOMException("<"+E_LOCKTYPE+"> must have exactly one child element");
}
lockInfo_lockType = ((Element)children.get(0)).getName();
}
/**
* Parses the <code><owner></code> part of the request content
* document.
*
* @param ownerElement the <code><owner></code> to parse.
*
* @throws JDOMException if parsing the request failed or if
* the request is not valid.
*/
private void parseOwner(Element ownerElement) throws JDOMException {
if (ownerElement == null) {
lockInfo_lockOwner = DEFAULT_LOCK_OWNER;
return;
// throw new JDOMException("Expected <"+E_OWNER+"> element");
}
StringWriter stringWriter = new StringWriter();
XMLOutputter xmlOutputter = new XMLOutputter();
try {
xmlOutputter.outputElementContent(ownerElement, stringWriter);
}
catch (IOException e) {
// this should not happen since we do no "real" I/O but
// only print to a PrintWriter
e.printStackTrace();
}
lockInfo_lockOwner = stringWriter.toString();
if (lockInfo_lockOwner.length() == 0) {
lockInfo_lockOwner = DEFAULT_LOCK_OWNER;
// throw new JDOMException("<"+E_OWNER+"> element must not be empty");
}
}
/**
* Execute request.
*
* @exception WebdavException Unrecoverable error while renewing lock
*/
protected void executeRequest()
throws WebdavException {
// Prevent dirty reads
slideToken.setForceStoreEnlistment(true);
SubjectNode toLockSubject = null;
boolean isCollection = isCollection(lockInfo_lockSubject);
boolean inheritance = false;
Date lockDate = null;
switch (lockType) {
case LOCK_CREATION:
try {
NamespaceConfig namespaceConfig = token.getNamespaceConfig();
try {
toLockSubject = (SubjectNode) structure
.retrieve(slideToken, lockInfo_lockSubject);
} catch (ObjectNotFoundException ex) {
// Creating a lock null resource
toLockSubject = new SubjectNode();
// Creating new subject
structure.create(slideToken, toLockSubject,
lockInfo_lockSubject);
NodeRevisionDescriptor revisionDescriptor =
new NodeRevisionDescriptor(0);
// Resource type
XMLValue lockNull =
new XMLValue(new Element(E_LOCKNULL, DNSP));
revisionDescriptor.setResourceType(lockNull.toString());
// Creating the revision descriptor
content.create(slideToken, lockInfo_lockSubject,
revisionDescriptor, null);
}
NodeLock lockToken = null;
inheritance = (depth != 0);
boolean exclusive =
!(lockInfo_lockScope.equals(E_SHARED));
if (lockDate == null)
lockDate = new Date((new Date()).getTime()
+ ((long)lockDuration * 1000L));
lockToken =
new NodeLock(toLockSubject, (SubjectNode)security.getPrincipal(slideToken),
namespaceConfig.getCreateObjectAction(),
lockDate, inheritance, exclusive, lockInfo_lockOwner);
lock.lock(slideToken, lockToken);
// Set the lock-token header
// [RFC 2518, 9.5] " The Lock-Token response header is used
// with the LOCK method to indicate the lock token created as
// a result of a successful LOCK request to create a new
// lock."
resp.setHeader("Lock-Token",
"<"+S_LOCK_TOKEN+lockToken.getLockId()+">");
resp.setStatus(WebdavStatus.SC_OK);
// The lock token on which the DAV module will have the info
showLockDiscoveryInfo(lockToken);
} catch (ObjectIsAlreadyLockedException e) {
if (inheritance && generateMultiStatusResponse(isCollection, e, requestUri)) {
// error is on the resource which we attempted to lock
String errorMessage = generateErrorMessage(e);
// Write it on the servlet writer
resp.setContentType(TEXT_XML_UTF_8);
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
try {
resp.getWriter().write(errorMessage);
} catch(IOException ex) {
// Critical error ... Servlet container is dead or something
int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
} else {
// Returning 207 on non-collection requests is generally
// considered bad. So let's not do it, since this way
// makes clients generally behave better.
resp.setStatus(WebdavStatus.SC_LOCKED);
}
//
// make sure the transaction is aborted
// throw any WebDAV exception to indicate the transaction wants to be aborted
//
throw new WebdavException(WebdavStatus.SC_ACCEPTED, false);
} catch (Exception e) {
int statusCode = getErrorCode( e );
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
break;
case LOCK_REFRESH:
try {
Enumeration lockTokens =
lock.enumerateLocks(slideToken, lockInfo_lockSubject, false);
NodeLock currentLockToken = null;
Date newExpirationDate =
new Date((new Date()).getTime() + ((long)lockDuration * 1000L));
while (lockTokens.hasMoreElements()) {
currentLockToken = (NodeLock) lockTokens.nextElement();
lock.renew(slideToken, currentLockToken,
newExpirationDate);
}
showLockDiscoveryInfo(currentLockToken);
} catch (SlideException e) {
int statusCode = WebdavStatus.SC_PRECONDITION_FAILED;
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
break;
}
}
/**
* Get return status based on exception type.
*/
protected int getErrorCode(Exception ex) {
try {
throw ex;
} catch (ObjectNotFoundException e) {
return WebdavStatus.SC_PRECONDITION_FAILED;
} catch (Exception e) {
return super.getErrorCode(e);
}
}
/**
* Show lockdiscovery info.
*
* @exception WebdavException Something is wrong with the servlet container
*/
protected void showLockDiscoveryInfo(NodeLock token)
throws WebdavException {
// Generating XML response
org.jdom.Element prop = new org.jdom.Element(E_PROP, DNSP);
org.jdom.Element lockdiscovery = new org.jdom.Element(E_LOCKDISCOVERY, DNSP);
prop.addContent(lockdiscovery);
XMLValue xmlValue = propertyHelper.computeLockDiscovery(token,
req.getServletPath(),
req.getContextPath());
Iterator iterator = xmlValue.iterator();
while (iterator.hasNext()) {
lockdiscovery.addContent((org.jdom.Element)iterator.next());
}
try {
//System.out.println("Query result");
//System.out.println(generatedXML.toString());
resp.setContentType(TEXT_XML_UTF_8);
Writer writer = resp.getWriter();
new org.jdom.output.XMLOutputter(XML_REPONSE_INDENT, true).output(new org.jdom.Document(prop), writer);
writer.flush();
} catch (Exception e) {
int statusCode = WebdavStatus.SC_INTERNAL_SERVER_ERROR;
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
}
/**
* Returns true
*/
protected boolean methodNeedsTransactionSupport() {
return true;
}
}