/*
* $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/PropPatchMethod.java,v 1.20 2001/08/31 03:21:45 remm Exp $
* $Revision: 1.20 $
* $Date: 2001/08/31 03:21:45 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.slide.webdav.method;
import java.security.Principal;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.apache.util.XMLPrinter;
import org.apache.util.DOMWriter;
import org.apache.util.WebdavStatus;
import org.apache.slide.authenticate.CredentialsToken;
import org.apache.slide.common.*;
import org.apache.slide.security.AccessDeniedException;
import org.apache.slide.webdav.*;
import org.apache.slide.structure.*;
import org.apache.slide.lock.*;
import org.apache.slide.content.*;
/**
* PROPPATCH method.
*
* @author <a href="mailto:remm@apache.org">Remy Maucherat</a>
*/
public class PropPatchMethod extends WebdavMethod {
// -------------------------------------------------------------- Constants
private static final int SET = 0;
private static final int REMOVE = 1;
// ----------------------------------------------------- Instance Variables
/**
* Properties to set. Vector of SlideProperty objects.
*/
private Vector propertiesToSet;
/**
* Properties to remove. Vector of String.
*/
private Vector propertiesToRemove;
/**
* DOM Writer.
*/
private DOMWriter domWriter;
/**
* Resource which will have its properties updated.
*/
private String resourcePath;
// ----------------------------------------------------------- Constructors
/**
* PROPPATCH method constructor.
*
* @param token Namespace access token
* @param req HTTP request
* @param resp HTTP response
*/
public PropPatchMethod(NamespaceAccessToken token, HttpServletRequest req,
HttpServletResponse resp,
WebdavServletConfig config) {
super(token, req, resp, config);
readRequestContent();
}
// ------------------------------------------------------ Protected Methods
/**
* Parse the request.
*
* @exception WebdavException Bad request
*/
protected void parseRequest()
throws WebdavException {
resourcePath = requestUri;
if (resourcePath == null) {
resourcePath = "/";
}
propertiesToSet = new Vector();
propertiesToRemove = new Vector();
if (requestBody.length() != 0) {
try {
Node setNode = null;
Node removeNode = null;
Document document = parseRequestContent();
// Get the root element of the document
Element rootElement = document.getDocumentElement();
NodeList childList = rootElement.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
if (currentNode.getNodeName().endsWith("set")) {
NodeList tempList =
currentNode.getChildNodes();
for (int j=0; j < tempList.getLength(); j++) {
switch (tempList.item(j).getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
if (tempList.item(j).getNodeName()
.endsWith("prop")) {
parseSetNode(tempList.item(j));
}
break;
}
}
}
if (currentNode.getNodeName().endsWith("remove")) {
NodeList tempList = currentNode.getChildNodes();
for (int j=0; j < tempList.getLength(); j++) {
switch (tempList.item(j).getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
if (tempList.item(j).getNodeName()
.endsWith("prop")) {
parseRemoveNode(tempList.item(j));
}
break;
}
}
}
break;
}
}
} catch (SAXException e) {
resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
throw new WebdavException(WebdavStatus.SC_BAD_REQUEST);
} catch (ParserConfigurationException e) {
System.err.println(e.getMessage());
resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
throw new WebdavException
(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
} catch (IOException e) {
System.err.println(e.getMessage());
e.printStackTrace();
resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
throw new WebdavException
(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
}
} else {
try {
resp.sendError(WebdavStatus.SC_BAD_REQUEST,
WebdavStatus.getStatusText
(WebdavStatus.SC_BAD_REQUEST));
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Execute the request.
*
* @exception WebdavException
*/
protected void executeRequest()
throws WebdavException {
// Prevent dirty reads
slideToken.setForceStoreEnlistment(true);
try {
NodeRevisionDescriptors revisionDescriptors =
content.retrieve(slideToken, resourcePath);
NodeRevisionDescriptor revisionDescriptor = null;
try {
revisionDescriptor =
content.retrieve(slideToken, revisionDescriptors);
} catch (RevisionDescriptorNotFoundException ex) {
revisionDescriptor = new NodeRevisionDescriptor(0);
// TODO : Create the initial revision ?
}
// Modifying the properties
Enumeration propertyList = null;
propertyList = propertiesToSet.elements();
while (propertyList.hasMoreElements()) {
Property currentProperty =
(Property) propertyList.nextElement();
if (checkProperty(currentProperty, SET)) {
NodeProperty newProperty =
new NodeProperty(currentProperty.name,
currentProperty.value,
currentProperty.namespace);
revisionDescriptor.setProperty(newProperty);
}
}
propertyList = propertiesToRemove.elements();
while (propertyList.hasMoreElements()) {
Property currentProperty =
(Property) propertyList.nextElement();
if (checkProperty(currentProperty, REMOVE)) {
revisionDescriptor.removeProperty(currentProperty.name);
}
}
content.store(slideToken, resourcePath, revisionDescriptor, null);
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
} catch (Exception e) {
resp.setStatus(getErrorCode(e)); // special handling needed
throw new WebdavException(WebdavStatus.SC_ACCEPTED, false); // abort the TA
}
// No serious errors. Printing the XML report.
writeReport();
}
/**
* Get return status based on exception type.
*/
protected int getErrorCode(Exception ex) {
try {
throw ex;
} catch (LinkedObjectNotFoundException e) {
return WebdavStatus.SC_NOT_FOUND;
} catch (Exception e) {
return super.getErrorCode(e);
}
}
// -------------------------------------------------------- Private Methods
/**
* Parse the set property node given.
*/
private void parseSetNode(Node setNode) {
NodeList childList = setNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
Property propertyToSet = getProperty(currentNode);
StringWriter writer = new StringWriter();
domWriter = new DOMWriter(writer, true);
// Printing all child nodes using the DOM printer
NodeList childNodes = currentNode.getChildNodes();
Node childNode = childNodes.item(0);
int pos = 0;
while (childNode != null) {
domWriter.print(childNode);
childNode = childNodes.item(++pos);
}
String nodeValue = writer.toString();
propertyToSet.value = nodeValue;
propertiesToSet.addElement(propertyToSet);
break;
}
}
}
/**
* Parse the remove property node given.
*/
private void parseRemoveNode(Node removeNode) {
NodeList childList = removeNode.getChildNodes();
for (int i=0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
Property propertyToRemove = getProperty(currentNode);
propertiesToRemove.addElement(propertyToRemove);
break;
}
}
}
/**
* Parse the namespace info of a node name.
*
* @param node The DOM node to parse
* @return The corresponding Property object
*/
private Property getProperty(Node node) {
Property property = new Property();
property.name = node.getLocalName();
property.namespace = node.getNamespaceURI();
property.namespaceAbbrev = node.getPrefix();
return property;
}
/**
* Check if the property is a live property which should have its value
* enforced by the server.
*
* @param property The property object
* @param actionType Can be either SET or REMOVE
*/
private boolean checkProperty(Property property, int actionType) {
// Checking the standard DAV properties which can't be modified using
// a propatch.
if (
(property.name.equalsIgnoreCase("creationdate")) ||
(property.name.equalsIgnoreCase("getcontentlength")) ||
(property.name.equalsIgnoreCase("getetag")) ||
(property.name.equalsIgnoreCase("getlastmodified")) ||
(property.name.equalsIgnoreCase("lockdiscovery"))
) {
property.status = WebdavStatus.SC_CONFLICT;
return false;
}
switch (actionType) {
case SET:
break;
case REMOVE:
break;
}
return true;
}
/**
* Write the report.
*/
private void writeReport()
throws WebdavException {
// Create multistatus object
XMLPrinter generatedXML = new XMLPrinter();
generatedXML.writeXMLHeader();
generatedXML.writeElement("d", "DAV", "multistatus",
XMLPrinter.OPENING);
generatedXML.writeElement("d", null, "response", XMLPrinter.OPENING);
generatedXML.writeProperty("d", null, "href",
WebdavUtils.encodeURL(requestUri));
// Parse the two properties list, and printout their status
Enumeration propertyList = null;
propertyList = propertiesToSet.elements();
while(propertyList.hasMoreElements()) {
Property property = (Property) propertyList.nextElement();
generatedXML.writeElement("d", null, "propstat",
XMLPrinter.OPENING);
generatedXML.writeElement("d", null, "prop", XMLPrinter.OPENING);
generatedXML.writeElement(property.namespaceAbbrev,
property.namespace, property.name,
XMLPrinter.NO_CONTENT);
generatedXML.writeElement("d", null, "prop", XMLPrinter.CLOSING);
generatedXML.writeProperty
("d", null, "status", "HTTP/1.1 " + property.status + " "
+ WebdavStatus.getStatusText(property.status));
generatedXML.writeElement("d", null, "propstat",
XMLPrinter.CLOSING);
}
propertyList = propertiesToRemove.elements();
while(propertyList.hasMoreElements()) {
Property property = (Property) propertyList.nextElement();
generatedXML.writeElement("d", null, "propstat",
XMLPrinter.OPENING);
generatedXML.writeElement("d", null, "prop", XMLPrinter.OPENING);
generatedXML.writeElement(property.namespaceAbbrev,
property.namespace, property.name,
XMLPrinter.NO_CONTENT);
generatedXML.writeElement("d", null, "prop", XMLPrinter.CLOSING);
generatedXML.writeProperty
("d", null, "status", "HTTP/1.1 " + property.status + " "
+ WebdavStatus.getStatusText(property.status));
generatedXML.writeElement("d", null, "propstat",
XMLPrinter.CLOSING);
}
generatedXML.writeElement("d", null, "response", XMLPrinter.CLOSING);
generatedXML.writeElement("d", "multistatus", XMLPrinter.CLOSING);
try {
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.flush();
} catch (Exception e) {
e.printStackTrace();
throw new WebdavException(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
}
}
// --------------------------------------------------- Property Inner Class
private class Property {
public String name;
public String value;
public String namespace;
public String namespaceAbbrev;
public int status = WebdavStatus.SC_OK;
}
/**
* Returns true
*/
protected boolean methodNeedsTransactionSupport() {
return true;
}
}