/* * $Header: /home/cvspublic/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/MoveMethod.java,v 1.72.2.2 2004/10/18 08:42:58 pnever Exp $ * $Revision: 1.72.2.2 $ * $Date: 2004/10/18 08:42:58 $ * * ==================================================================== * * 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.Iterator; import org.apache.slide.common.NamespaceAccessToken; import org.apache.slide.common.ServiceAccessException; import org.apache.slide.common.SlideException; import org.apache.slide.content.NodeProperty; import org.apache.slide.content.NodeRevisionDescriptor; import org.apache.slide.content.NodeRevisionDescriptors; import org.apache.slide.content.NodeRevisionNumber; import org.apache.slide.content.RevisionDescriptorNotFoundException; import org.apache.slide.content.RevisionNotFoundException; import org.apache.slide.content.NodeProperty.NamespaceCache; import org.apache.slide.event.EventDispatcher; import org.apache.slide.macro.CopyListener; import org.apache.slide.macro.DeleteListener; import org.apache.slide.macro.Macro; import org.apache.slide.macro.MacroException; import org.apache.slide.macro.MacroParameters; import org.apache.slide.search.RequestedResource; import org.apache.slide.search.Search; import org.apache.slide.search.SearchQuery; import org.apache.slide.search.SearchQueryResult; import org.apache.slide.structure.ObjectNode; import org.apache.slide.structure.ObjectNotFoundException; import org.apache.slide.util.Configuration; import org.apache.slide.webdav.WebdavException; import org.apache.slide.webdav.WebdavServletConfig; import org.apache.slide.webdav.event.WebdavEvent; import org.apache.slide.webdav.util.WebdavUtils; import org.apache.slide.webdav.util.DaslConstants; 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.WebdavStatus; import org.apache.slide.webdav.util.resourcekind.AbstractResourceKind; import org.apache.slide.webdav.util.resourcekind.CheckedOutVersionControlled; import org.apache.slide.webdav.util.resourcekind.DeltavCompliantUnmappedUrl; import org.apache.slide.webdav.util.resourcekind.ResourceKind; import org.apache.slide.webdav.util.resourcekind.VersionControlled; import org.apache.slide.webdav.util.resourcekind.Working; import org.jdom.Element; import org.jdom.JDOMException; /** * MOVE Method. * */ public class MoveMethod extends AbstractMultistatusResponseMethod implements DeltavConstants, DeleteListener, CopyListener, WriteMethod { /** * The VersioningHelper used by this instance. */ protected VersioningHelper versioningHelper = null; /** * The PropertyHelper used by this instance. */ protected PropertyHelper propertyHelper = null; /** * Indicates if the source of the MOVE request is a workspace. */ protected boolean isRequestSourceWorkspace = false; /** * Indicates if the source of the current copy operation is a VCR. * Used by {@link #beforeCopy beforeCopy()} and {@link #afterCopy afterCopy()}. */ protected boolean isCopySourceVCR = false; /** * The Element returned by {@link #getWorkingResourceSearchElement * getWorkingResourceSearchElement()}. */ protected Element basicSearch = null; /** * The <literal> Element used in the basic search query returned by * {@link #getWorkingResourceSearchElement getWorkingResourceSearchElement()}. */ protected Element literal = null; private boolean destinationExistsBefore = false; // ----------------------------------------------------------- Constructors /** * Constructor. * * @param token the token for accessing the namespace * @param config configuration of the WebDAV servlet */ public MoveMethod(NamespaceAccessToken token, WebdavServletConfig config) { super(token, config); } // ------------------------------------------------------ Protected Methods /** * Execute request. * * @exception WebdavException Unrecoverable error while moving the files */ protected void executeRequest() throws WebdavException, IOException { versioningHelper = VersioningHelper.getVersioningHelper(slideToken, token, req, resp, config); propertyHelper = PropertyHelper.getPropertyHelper(slideToken, token, getConfig()); sourceUri = WebdavUtils.cleanParameterString(sourceUri); destinationUri = WebdavUtils.cleanParameterString(destinationUri); // Prevent dirty reads slideToken.setForceStoreEnlistment(true); // check lock-null resources try { if (isLockNull(sourceUri)) { int statusCode = WebdavStatus.SC_NOT_FOUND; sendError( statusCode, "lock-null resource", new Object[]{sourceUri} ); throw new WebdavException( statusCode ); } else if (isLocked(sourceUri)) { int statusCode = WebdavStatus.SC_LOCKED; sendError( statusCode, getClass().getName()+".noLocked", new Object[]{sourceUri} ); throw new WebdavException( statusCode ); } } catch (ServiceAccessException e) { int statusCode = getErrorCode((Exception)e); sendError( statusCode, e ); throw new WebdavException( statusCode ); } int depth = requestHeaders.getDepth(INFINITY); if (depth < INFINITY) { int sc = WebdavStatus.SC_PRECONDITION_FAILED; sendError( sc, "Invalid header Depth: "+depth ); throw new WebdavException( sc ); } MacroParameters macroParameters = null; boolean isCollection = isCollection(sourceUri); if (overwrite) { macroParameters = Macro.RECURSIVE_OVERWRITE_PARAMETERS; } else { macroParameters = Macro.DEFAULT_PARAMETERS; } // check destination URI UriHandler destinationUriHandler = UriHandler.getUriHandler(destinationUri); if (destinationUriHandler.isRestrictedUri()) { int statusCode = WebdavStatus.SC_FORBIDDEN; sendError( statusCode, getClass().getName()+".restrictedDestinationUri", new Object[]{destinationUri} ); throw new WebdavException( statusCode ); } UriHandler sourceUriHandler = UriHandler.getUriHandler(sourceUri); isRequestSourceWorkspace = sourceUriHandler.isWorkspaceUri(); try { // check preconditions ViolatedPrecondition violatedPrecondition = getPreconditionViolation(sourceUri, destinationUri); if (violatedPrecondition != null) { PreconditionViolationException e = new PreconditionViolationException(violatedPrecondition, sourceUri); sendPreconditionViolation(e); throw e; } destinationExistsBefore = exists(destinationUri); if (!overwrite && destinationExistsBefore) { int statusCode = WebdavStatus.SC_PRECONDITION_FAILED; sendError( statusCode, getClass().getName()+".noOverwrite", new Object[]{destinationUri} ); throw new WebdavException( statusCode ); } if ( WebdavEvent.MOVE.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.MOVE, new WebdavEvent(this)); macro.move(slideToken, sourceUri, destinationUri, macroParameters, null, this, null, this); if (overwrite && destinationExistsBefore) { resp.setStatus(WebdavStatus.SC_NO_CONTENT); } else { resp.setStatus(WebdavStatus.SC_CREATED); } } catch (MacroException e) { if(generateMultiStatusResponse(isCollection, e, requestUri)) { String errorMessage = generateErrorMessage(e); // Write it on the servlet writer resp.setStatus(WebdavStatus.SC_MULTI_STATUS); try { resp.setContentType(TEXT_XML_UTF_8); 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. SlideException exception = (SlideException)e.enumerateExceptions().nextElement(); if (exception instanceof PreconditionViolationException) { try { sendPreconditionViolation((PreconditionViolationException)exception); } 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 { int statusCode = getErrorCode( exception ); sendError( statusCode, exception ); throw new WebdavException( statusCode ); } } // // 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 (SlideException e) { int statusCode = getErrorCode( e ); sendError( statusCode, e ); throw new WebdavException( statusCode ); } } /** * Get return status based on exception type. */ protected int getErrorCode(SlideException ex) { try { throw ex; } catch(RevisionNotFoundException e) { return WebdavStatus.SC_NOT_FOUND; } catch (SlideException e) { return super.getErrorCode(e); } } /** * Checks the (DeltaV) preconditions *
sourceUri
. The copy can be prohibited by
* throwing a SlideException.
*
* @param sourceUri the Uri of the resource that will be copied.
* @param destinationUri the Uri of the copy.
*
* @throws SlideException this Exception will be passed to the caller
* of the Macro helper (contained in the
* MacroDeleteException.
*/
public void beforeCopy(String sourceUri, String destinationUri, boolean isRootOfCopy) throws SlideException {
if( Configuration.useVersionControl() ) {
UriHandler uriHandler = UriHandler.getUriHandler(sourceUri);
if (uriHandler.isVersionUri()) {
throw new PreconditionViolationException(new ViolatedPrecondition(DeltavConstants.C_CANNOT_RENAME_VERSION,
WebdavStatus.SC_FORBIDDEN),
sourceUri);
}
if (uriHandler.isHistoryUri()) {
throw new PreconditionViolationException(new ViolatedPrecondition(DeltavConstants.C_CANNOT_RENAME_HISTORY,
WebdavStatus.SC_FORBIDDEN),
sourceUri);
}
if (uriHandler.isWorkingresourceUri()) {
throw new PreconditionViolationException(new ViolatedPrecondition(DeltavConstants.C_CANNOT_RENAME_WORKING_RESOURCE,
WebdavStatus.SC_FORBIDDEN),
sourceUri);
}
NodeRevisionDescriptors sourceRevisionDescriptors =
content.retrieve( slideToken, sourceUri);
NodeRevisionDescriptor sourceRevisionDescriptor =
content.retrieve( slideToken, sourceRevisionDescriptors);
ResourceKind resourceKind = AbstractResourceKind.determineResourceKind(token,
sourceRevisionDescriptors,
sourceRevisionDescriptor);
isCopySourceVCR = (resourceKind instanceof VersionControlled);
// if resource being moved is a checked-out VCR or a WR,
// update its URI in the DAV:checkout-set property of the VR
if(resourceKind instanceof CheckedOutVersionControlled || resourceKind instanceof Working) {
String vrUri =
VersioningHelper.getUriOfAssociatedVR(token, slideToken, content, sourceUri);
NodeRevisionDescriptors vrNrds = content.retrieve(slideToken, vrUri);
NodeRevisionDescriptor vrNrd = content.retrieve(slideToken, vrNrds);
try {
PropertyHelper.removeHrefFromProperty(vrNrd, P_CHECKOUT_SET, sourceUri);
PropertyHelper.addHrefToProperty(vrNrd, P_CHECKOUT_SET, destinationUri);
content.store(slideToken, vrNrds.getUri(), vrNrd, null);
}
catch (JDOMException e) {
throw new SlideException(
"Unable to update DAV:checkout-set of "+vrUri+": "+e.getMessage() );
}
}
}
}
/**
* This method is called after copying the resource to
* the given destinationUri
.
*
* @param sourceUri the Uri of the resource that has been copied.
* @param destinationUri the Uri of the copy.
*
* @throws SlideException this Exception will be passed to the caller
* of the Macro helper (contained in the
* MacroDeleteException.
*/
public void afterCopy(String sourceUri, String destinationUri, boolean isRootOfCopy, boolean destinationExists) throws SlideException {
if( Configuration.useVersionControl() ) {
NodeRevisionDescriptors sourceNrds =
content.retrieve( slideToken, sourceUri);
NodeRevisionDescriptors destinationNrds =
content.retrieve( slideToken, destinationUri);
NodeRevisionDescriptor destinationNrd =
content.retrieve( slideToken, destinationNrds );
// copy DeltaV-specific "0.0" revision if exists
try {
NodeRevisionDescriptor sourceNrd00 =
content.retrieve( slideToken, sourceNrds, NodeRevisionNumber.HIDDEN_0_0 );
NodeRevisionDescriptor destinationNrd00 = sourceNrd00.cloneObject();
try {
content.retrieve( slideToken, destinationNrds, NodeRevisionNumber.HIDDEN_0_0 );
content.store( slideToken, destinationUri, destinationNrd00, null ); // revisionContent=null
}
catch( RevisionDescriptorNotFoundException x ) {
content.create( slideToken, destinationUri, null, destinationNrd00, null ); // branch=null, revisionContent=null
}
}
catch (ServiceAccessException e) {
throw e;
}
catch (SlideException e) {}
handleWorkspacePostconditions(destinationNrd, destinationUri);
handleWorkingResourcePostconditions(sourceUri, destinationUri);
}
}
/**
* Handles the working resource postconditions.
* resourcePath
.
*
* @param resourcePath the Uri to search for.
*
* @return the query document.
*/
protected Element getWorkingResourceSearchElement(String resourcePath) {
if (basicSearch == null) {
basicSearch = new Element(DaslConstants.E_BASICSEARCH,
DNSP);
Element select = new Element(DaslConstants.E_SELECT,
DNSP);
basicSearch.addContent(select);
Element prop = new Element(E_PROP,
DNSP);
select.addContent(prop);
Element autoUpdate = new Element(P_AUTO_UPDATE,
DNSP);
prop.addContent(autoUpdate);
Element from = new Element(DaslConstants.E_FROM,
DNSP);
basicSearch.addContent(from);
Element scope = new Element(DaslConstants.E_SCOPE,
DNSP);
from.addContent(scope);
Element href = new Element(E_HREF,
DNSP);
scope.addContent(href);
href.setText("");
Iterator excludeIterator = propertyHelper.getNonVcrPathExcludeList().iterator();
while (excludeIterator.hasNext()) {
scope.addContent((Element)excludeIterator.next());
}
Element where = new Element(DaslConstants.E_WHERE,
DNSP);
basicSearch.addContent(where);
Element propcontains = new Element(DaslConstants.E_PROPCONTAINS,
NamespaceCache.SLIDE_NAMESPACE);
where.addContent(propcontains);
propcontains.addContent((Element)prop.clone());
literal = new Element(DaslConstants.E_LITERAL,
DNSP);
propcontains.addContent(literal);
}
literal.setText(resourcePath);
return basicSearch;
}
/**
* Handles the workspace postconditions
* targetUri
. The deletion can be prohibited by
* throwing a SlideException.
*
* @param targetUri the Uri of the resource that will be deleted.
*
* @throws SlideException this Exception will be passed to the caller
* of the Macro helper (contained in the
* MacroDeleteException.
*/
public void beforeDelete(String targetUri) throws SlideException {
}
/**
* This method is called after deleting the resource associated by
* the given targetUri
.
*
* @param targetUri the Uri of the resource that will be deleted.
*
* @throws SlideException this Exception will be passed to the caller
* of the Macro helper (contained in the
* MacroDeleteException.
*/
public void afterDelete(String targetUri) throws SlideException {
}
}