/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/PutMethod.java,v 1.69.2.1 2004/02/05 16:11:23 mholz Exp $
* $Revision: 1.69.2.1 $
* $Date: 2004/02/05 16:11:23 $
*
* ====================================================================
*
* 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.util.Date;
import org.apache.slide.common.Domain;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.SlideException;
import org.apache.slide.common.SlideToken;
import org.apache.slide.content.NodeProperty;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionAlreadyExistException;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.structure.LinkedObjectNotFoundException;
import org.apache.slide.structure.ObjectAlreadyExistsException;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.Configuration;
import org.apache.slide.webdav.WebdavException;
import org.apache.slide.webdav.WebdavServletConfig;
import org.apache.slide.webdav.util.DeltavConstants;
import org.apache.slide.webdav.util.PreconditionViolationException;
import org.apache.slide.webdav.util.PropertyHelper;
import org.apache.slide.webdav.util.UriHandler;
import org.apache.slide.webdav.util.VersioningHelper;
import org.apache.slide.webdav.util.ViolatedPrecondition;
import org.apache.slide.webdav.util.WebdavUtils;
import org.apache.slide.webdav.util.resourcekind.AbstractResourceKind;
import org.apache.slide.webdav.util.resourcekind.CheckedInVersionControlled;
import org.apache.slide.webdav.util.resourcekind.ResourceKind;
import org.apache.util.WebdavStatus;
/**
* PUT method.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
*/
public class PutMethod
extends AbstractWebdavMethod
implements DeltavConstants {
// -------------------------------------------------------------- Constants
public final static boolean AUTO_VERSION_CONTROL = new Boolean(
Domain.getParameter(I_AUTO_VERSION_CONTROL, I_AUTO_VERSION_CONTROL_DEFAULT) ).booleanValue();
// ----------------------------------------------------- Instance Variables
/**
* The VersioningHelper used by this instance.
*/
protected VersioningHelper versioningHelper = null;
/**
* Resource to be written.
*/
protected String resourcePath;
private boolean isInVersioncontrolExcludePath = false;
// ----------------------------------------------------------- Constructors
/**
* Constructor.
*
* @param token the token for accessing the namespace
* @param config configuration of the WebDAV servlet
*/
public PutMethod(NamespaceAccessToken token, WebdavServletConfig config) {
super(token, config);
}
// ------------------------------------------------------ Protected Methods
/**
* Parse XML request.
*
* @exception WebdavException Does not happen
*/
protected void parseRequest()
throws WebdavException {
versioningHelper = VersioningHelper.getVersioningHelper(slideToken,
token,
req,
resp,
config);
resourcePath = requestUri;
if (resourcePath == null) {
resourcePath = "/";
}
}
/**
* Execute request.
*
* @exception WebdavException Bad request
*/
protected void executeRequest()
throws WebdavException, IOException {
// Prevent dirty reads
slideToken.setForceStoreEnlistment(true);
// check destination URI
UriHandler destUh = UriHandler.getUriHandler(resourcePath);
if( VersionControlMethod.VERSIONCONTROL_EXCLUDEPATH != null && VersionControlMethod.VERSIONCONTROL_EXCLUDEPATH.length() > 0 ) {
UriHandler exUh = UriHandler.getUriHandler( VersionControlMethod.VERSIONCONTROL_EXCLUDEPATH );
if( exUh.isAncestorOf(destUh) )
isInVersioncontrolExcludePath = true;
}
if (destUh.isRestrictedUri()) {
boolean sendError = true;
if( destUh.isWorkingresourceUri() ) {
// PUT on existing WRs is *not* restricted !!!
try {
content.retrieve(slideToken, resourcePath);
sendError = false;
}
catch( Exception x ) {};
}
if( sendError ) {
int statusCode = WebdavStatus.SC_FORBIDDEN;
sendError( statusCode, getClass().getName()+".restrictedDestinationUri", new Object[]{resourcePath} );
throw new WebdavException( statusCode );
}
}
try {
try {
boolean isLockedNullResource = false;
NodeRevisionDescriptors revisionDescriptors =
content.retrieve(slideToken, resourcePath);
NodeRevisionNumber revisionNumber =
revisionDescriptors.getLatestRevision();
NodeRevisionDescriptor oldRevisionDescriptor = null;
if (revisionNumber != null) {
try {
oldRevisionDescriptor = content.retrieve
(slideToken, revisionDescriptors);
} catch (RevisionDescriptorNotFoundException e) {
}
}
if (WebdavUtils.isCollection(oldRevisionDescriptor)) {
int statusCode = WebdavStatus.SC_METHOD_NOT_ALLOWED;
sendError( statusCode, getClass().getName()+".mustNotBeCollection" );
throw new WebdavException( statusCode );
}
NodeRevisionDescriptor revisionDescriptor = null;
if (oldRevisionDescriptor == null) {
revisionDescriptor = new NodeRevisionDescriptor();
} else {
revisionDescriptor = oldRevisionDescriptor;
revisionDescriptor.setContentLength(-1);
}
ResourceKind resourceKind = AbstractResourceKind.determineResourceKind(token, resourcePath, revisionDescriptor);
versioningHelper.isWriteLocked(slideToken, revisionDescriptors);
// check preconditions
ViolatedPrecondition violatedPrecondition = getPreconditionViolation(revisionDescriptors, revisionDescriptor, resourceKind);
if (violatedPrecondition != null) {
throw new PreconditionViolationException(violatedPrecondition, resourcePath);
}
// Changed for DeltaV --start--
boolean mustCheckIn = false;
if( Configuration.useVersionControl() &&
(resourceKind instanceof CheckedInVersionControlled) &&
versioningHelper.mustCheckoutAutoVersionedVCR(revisionDescriptors, revisionDescriptor) ) {
versioningHelper.checkout(revisionDescriptors, revisionDescriptor, false, false, true );
mustCheckIn = versioningHelper.mustCheckinAutoVersionedVCR(slideToken, revisionDescriptors, revisionDescriptor);
}
// Changed for DeltaV --end--
NodeRevisionContent revisionContent =
new NodeRevisionContent();
//revisionContent.setContent(req.getReader());
revisionContent.setContent(req.getInputStream());
// Get content length
revisionDescriptor.setContentLength(req.getContentLength());
// Last modification date
revisionDescriptor.setLastModified(new Date());
// Etag generation
revisionDescriptor.setETag(PropertyHelper.computeEtag(resourcePath, revisionDescriptor) );
// Get content type (allow content-type to be updated here)
String contentType = req.getContentType();
if (contentType == null) {
contentType = getConfig().getServletContext()
.getMimeType(resourcePath);
}
if (contentType == null) {
contentType = getConfig().getDefaultMimeType();
}
revisionDescriptor.setContentType(contentType);
// Normally assume the 'getcontentlanguage'
// is set, however, before we clear the
// 'resourcetype' need to check for the case when a
// 'lock-null' is created just before the initial PUT. In
// that case need to add the missing properties.
if (isLockNull(revisionDescriptor)) {
// if (revisionDescriptor.getResourceType().equals("<lock-null/>")) {
isLockedNullResource = true;
revisionDescriptor.setContentLanguage("en");
// Changed for DeltaV --start--
if( Configuration.useVersionControl() ) {
// Workspace
versioningHelper.setWorkspaceProperty( resourcePath, revisionDescriptor );
}
// Changed for DeltaV --end--
}
// Resource type
revisionDescriptor.setResourceType("");
// Owner
if ( isLockedNullResource ) {
// set the owner when updating a lock-null resource
String creator = ((SubjectNode)security.getPrincipal(slideToken)).getPath().lastSegment();
revisionDescriptor.setCreationUser(creator);
revisionDescriptor.setOwner(creator);
}
content.store(slideToken, resourcePath, revisionDescriptor,
revisionContent);
// Changed for DeltaV --start--
// check if the resource should be put under version-control
if ( isLockedNullResource ) {
if ( Configuration.useVersionControl() && AUTO_VERSION_CONTROL && !isInVersioncontrolExcludePath ) {
versioningHelper.versionControl(resourcePath);
}
}
if( Configuration.useVersionControl() && mustCheckIn) {
versioningHelper.checkin(revisionDescriptors, revisionDescriptor, false, false, true ); //forkOk=false, keepCheckedOut=false
}
// Changed for DeltaV --end--
// ETag header
resp.setHeader("ETag", revisionDescriptor.getETag() );
resp.setStatus(WebdavStatus.SC_NO_CONTENT);
} catch (LinkedObjectNotFoundException e) {
int statusCode = getErrorCode( e );
sendError( statusCode, e );
throw new WebdavException( statusCode );
} catch (ObjectNotFoundException e) {
SubjectNode subject = new SubjectNode();
// Creating an object
structure.create(slideToken, subject, resourcePath);
NodeRevisionDescriptor revisionDescriptor =
new NodeRevisionDescriptor(req.getContentLength());
//NodeProperty property = null;
// Creation date
// Resource type
revisionDescriptor.setResourceType("");
// Source
revisionDescriptor.setSource("");
// Get content language
revisionDescriptor.setContentLanguage("en");
// Get content length
revisionDescriptor.setContentLength(req.getContentLength());
// Get content type
String contentType = req.getContentType();
if (contentType == null) {
contentType = getConfig().getServletContext()
.getMimeType(resourcePath);
}
if (contentType == null) {
contentType = getConfig().getDefaultMimeType();
}
revisionDescriptor.setContentType(contentType);
// Last modification date
revisionDescriptor.setLastModified(new Date());
// Etag generation
revisionDescriptor.setETag(PropertyHelper.computeEtag(resourcePath, revisionDescriptor));
// Creation date
revisionDescriptor.setCreationDate(new Date());
// Owner
String creator = ((SubjectNode)security.getPrincipal(slideToken)).getPath().lastSegment();
revisionDescriptor.setCreationUser(creator);
revisionDescriptor.setOwner(creator);
// Added for DeltaV --start--
if( Configuration.useVersionControl() ) {
// Workspace
versioningHelper.setWorkspaceProperty( resourcePath, revisionDescriptor );
}
// Added for DeltaV --end--
if (isMsProprietarySupport()) {
NodeProperty property = null;
// Is hidden
property = new NodeProperty("ishidden", "0", "MICROSOFT");
revisionDescriptor.setProperty(property);
// Is collection
property = new NodeProperty("iscollection", "0",
"MICROSOFT");
revisionDescriptor.setProperty(property);
// Is read only
property = new NodeProperty("isreadonly", "0",
"MICROSOFT");
revisionDescriptor.setProperty(property);
// Last accessed
property = new NodeProperty("lastaccessed",
(new Date()).toString(),
"MICROSOFT");
revisionDescriptor.setProperty(property);
}
// Creating revisionDescriptor associated with the object
NodeRevisionContent revisionContent =
new NodeRevisionContent();
revisionContent.setContent(req.getInputStream());
content.create(slideToken, resourcePath, revisionDescriptor,
revisionContent);
// check if the resource should be put under version-control
if ( Configuration.useVersionControl() && AUTO_VERSION_CONTROL && !isInVersioncontrolExcludePath ) {
versioningHelper.versionControl(resourcePath);
}
// ETag header
resp.setHeader("ETag", revisionDescriptor.getETag() );
resp.setStatus(WebdavStatus.SC_CREATED);
}
}
catch (PreconditionViolationException e) {
sendPreconditionViolation(e);
throw e;
}
catch (SlideException e) {
int statusCode = getErrorCode( e );
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
catch (Exception e) {
int statusCode = getErrorCode( e );
sendError( statusCode, e );
throw new WebdavException( statusCode );
}
}
/**
* Checks the (DeltaV) preconditions
* <ul>
* <li><DAV:cannot-modify-version-controlled-content></li>
* <li><DAV:cannot-modify-version></li>
* </ul>
*
* @param revisionDescriptors the NodeRevisionDescriptors of the resource
* to perform the <code>PUT</code> on.
* @param revisionDescriptor the NodeRevisionDescriptor of the resource
* to perform the <code>PUT</code> on.
* @param resourceKind the ResourceKind of the resource.
*
* @return the precondition that has been violated (if any).
*/
private ViolatedPrecondition getPreconditionViolation(NodeRevisionDescriptors revisionDescriptors, NodeRevisionDescriptor revisionDescriptor, ResourceKind resourceKind)
throws ServiceAccessException {
// use a non-blocking slide token.
SlideToken stoken = readonlySlideToken();
if( Configuration.useVersionControl() ) {
if (resourceKind instanceof CheckedInVersionControlled) {
// check precondition DAV:cannot-modify-version-controlled-content
String autoVersion = versioningHelper.getAutoVersionElementName(revisionDescriptor);
if (autoVersion == null) {
autoVersion = "";
}
if ( !E_CHECKOUT_CHECKIN.equals(autoVersion) &&
!E_CHECKOUT_UNLOCKED_CHECKIN.equals(autoVersion) &&
!E_CHECKOUT.equals(autoVersion) &&
!E_LOCKED_CHECKOUT.equals(autoVersion) ) {
return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION_CONTROLLED_CONTENT,
WebdavStatus.SC_FORBIDDEN);
}
if ( E_LOCKED_CHECKOUT.equals(autoVersion) &&
( !versioningHelper.isWriteLocked(stoken, revisionDescriptors) ) ) {
return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION_CONTROLLED_CONTENT,
WebdavStatus.SC_FORBIDDEN);
}
}
// check precondition DAV:cannot-modify-version
UriHandler uriHandler = UriHandler.getUriHandler(resourcePath);
if (uriHandler.isVersionUri()) {
return new ViolatedPrecondition(C_CANNOT_MODIFY_VERSION,
WebdavStatus.SC_FORBIDDEN);
}
}
return null;
}
/**
* Get return status based on exception type.
*/
protected int getErrorCode(SlideException ex) {
try {
throw ex;
} catch (RevisionAlreadyExistException e) {
return WebdavStatus.SC_CONFLICT;
} catch (ObjectAlreadyExistsException e) {
return WebdavStatus.SC_CONFLICT;
} catch (ObjectNotFoundException e) {
return WebdavStatus.SC_CONFLICT;
} catch (LinkedObjectNotFoundException e) {
return WebdavStatus.SC_NOT_FOUND;
} catch (SlideException e) {
return super.getErrorCode(e);
} catch (Exception e) {
return super.getErrorCode(e);
}
}
/**
* Returns true
*/
protected boolean methodNeedsTransactionSupport() {
return true;
}
}