package com.bradmcevoy.http.webdav; import com.bradmcevoy.http.*; import com.bradmcevoy.http.exceptions.BadRequestException; import com.bradmcevoy.http.exceptions.ConflictException; import com.bradmcevoy.http.exceptions.NotAuthorizedException; import com.bradmcevoy.http.webdav.PropPatchRequestParser.ParseResult; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bradmcevoy.http.Request.Method; import com.bradmcevoy.io.ReadingException; import com.bradmcevoy.io.WritingException; /** * Example request (from ms office) * * PROPPATCH /Documents/test.docx HTTP/1.1 content-length: 371 cache-control: no-cache connection: Keep-Alive host: milton:8080 user-agent: Microsoft-WebDAV-MiniRedir/6.0.6001 pragma: no-cache translate: f if: (<opaquelocktoken:900f718e-801c-4152-ae8e-f9395fe45d71>) content-type: text/xml; charset="utf-8" <?xml version="1.0" encoding="utf-8" ?> * <D:propertyupdate xmlns:D="DAV:" xmlns:Z="urn:schemas-microsoft-com:"> * <D:set> * <D:prop> * <Z:Win32LastAccessTime>Wed, 10 Dec 2008 21:55:22 GMT</Z:Win32LastAccessTime> * <Z:Win32LastModifiedTime>Wed, 10 Dec 2008 21:55:22 GMT</Z:Win32LastModifiedTime> * <Z:Win32FileAttributes>00000020</Z:Win32FileAttributes> * </D:prop> * </D:set> * </D:propertyupdate> * * * And another example request (from spec) * * <?xml version="1.0" encoding="utf-8" ?> <D:propertyupdate xmlns:D="DAV:" xmlns:Z="http://www.w3.com/standards/z39.50/"> <D:set> <D:prop> <Z:authors> <Z:Author>Jim Whitehead</Z:Author> <Z:Author>Roy Fielding</Z:Author> </Z:authors> </D:prop> </D:set> <D:remove> <D:prop><Z:Copyright-Owner/></D:prop> </D:remove> </D:propertyupdate> * * * Here is an example response (from the spec) * * HTTP/1.1 207 Multi-Status Content-Type: text/xml; charset="utf-8" Content-Length: xxxx <?xml version="1.0" encoding="utf-8" ?> <D:multistatus xmlns:D="DAV:" xmlns:Z="http://www.w3.com/standards/z39.50"> <D:response> <D:href>http://www.foo.com/bar.html</D:href> <D:propstat> <D:prop><Z:Authors/></D:prop> <D:status>HTTP/1.1 424 Failed Dependency</D:status> </D:propstat> <D:propstat> <D:prop><Z:Copyright-Owner/></D:prop> <D:status>HTTP/1.1 409 Conflict</D:status> </D:propstat> <D:responsedescription> Copyright Owner can not be deleted or altered.</D:responsedescription> </D:response> </D:multistatus> * * * @author brad */ public class PropPatchHandler implements ExistingEntityHandler { private final static Logger log = LoggerFactory.getLogger( PropPatchHandler.class ); private final ResourceHandlerHelper resourceHandlerHelper; private final PropPatchRequestParser requestParser; private final PropPatchSetter patchSetter; private final WebDavResponseHandler responseHandler; public PropPatchHandler( ResourceHandlerHelper resourceHandlerHelper, WebDavResponseHandler responseHandler, PropPatchSetter propPatchSetter ) { this.resourceHandlerHelper = resourceHandlerHelper; this.requestParser = new DefaultPropPatchParser(); patchSetter = propPatchSetter; this.responseHandler = responseHandler; } public PropPatchHandler( ResourceHandlerHelper resourceHandlerHelper, PropPatchRequestParser requestParser, PropPatchSetter patchSetter, WebDavResponseHandler responseHandler ) { this.resourceHandlerHelper = resourceHandlerHelper; this.requestParser = requestParser; this.patchSetter = patchSetter; this.responseHandler = responseHandler; } public String[] getMethods() { return new String[]{Method.PROPPATCH.code}; } public boolean isCompatible( Resource r ) { return patchSetter.supports( r ); } public void process( HttpManager httpManager, Request request, Response response ) throws ConflictException, NotAuthorizedException, BadRequestException { resourceHandlerHelper.process( httpManager, request, response, this ); } public void processResource( HttpManager manager, Request request, Response response, Resource r ) throws NotAuthorizedException, ConflictException, BadRequestException { resourceHandlerHelper.processResource( manager, request, response, r, this ); } public void processExistingResource( HttpManager manager, Request request, Response response, Resource resource ) throws NotAuthorizedException, BadRequestException, ConflictException { // todo: check if token header try { InputStream in = request.getInputStream(); ParseResult parseResult = requestParser.getRequestedFields( in ); String href = request.getAbsoluteUrl(); List<PropFindResponse> responses = new ArrayList<PropFindResponse>(); PropFindResponse resp = patchSetter.setProperties( href, parseResult, resource ); responses.add(resp); responseHandler.respondPropFind( responses, response, request, resource); } catch( WritingException ex ) { throw new RuntimeException( ex ); } catch( ReadingException ex ) { throw new RuntimeException( ex ); } catch( IOException ex ) { throw new RuntimeException( ex ); } } public static class Field { public final String name; String namespaceUri; public Field( String name ) { this.name = name; } public void setNamespaceUri( String namespaceUri ) { this.namespaceUri = namespaceUri; } public String getNamespaceUri() { return namespaceUri; } } public static class SetField extends Field { public final String value; public SetField( String name, String value ) { super( name ); this.value = value; } } public static class Fields implements Iterable<Field> { /** * fields to remove */ public final List<Field> removeFields = new ArrayList<Field>(); /** * fields to set to a value */ public final List<SetField> setFields = new ArrayList<PropPatchHandler.SetField>(); private int size() { return removeFields.size() + setFields.size(); } public Iterator<Field> iterator() { List<Field> list = new ArrayList<Field>( removeFields ); list.addAll( setFields ); return list.iterator(); } } }