Index: jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/JcrConstants.java
===================================================================
--- jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/JcrConstants.java (revision 700037)
+++ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/JcrConstants.java (working copy)
@@ -241,6 +241,10 @@
*/
public static final String MIX_VERSIONABLE = "mix:versionable";
/**
+ * mix:shareable
+ */
+ public static final String MIX_SHAREABLE = "mix:shareable";
+ /**
* nt:base
*/
public static final String NT_BASE = "nt:base";
Index: jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java
===================================================================
--- jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java (revision 700037)
+++ jackrabbit-webdav/src/test/java/org/apache/jackrabbit/webdav/server/BindTest.java (working copy)
@@ -22,6 +22,12 @@
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collections;
import junit.framework.TestCase;
@@ -32,16 +38,30 @@
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.MultiStatus;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+import org.apache.jackrabbit.webdav.bind.BindConstants;
+import org.apache.jackrabbit.webdav.bind.BindInfo;
+import org.apache.jackrabbit.webdav.bind.RebindInfo;
+import org.apache.jackrabbit.webdav.bind.UnbindInfo;
+import org.apache.jackrabbit.webdav.bind.ParentSet;
+import org.apache.jackrabbit.webdav.bind.ParentElement;
import org.apache.jackrabbit.webdav.client.methods.DavMethod;
import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
import org.apache.jackrabbit.webdav.client.methods.OptionsMethod;
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
import org.apache.jackrabbit.webdav.client.methods.VersionControlMethod;
+import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
+import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
+import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
+import org.apache.jackrabbit.webdav.client.methods.BindMethod;
+import org.apache.jackrabbit.webdav.client.methods.RebindMethod;
+import org.apache.jackrabbit.webdav.client.methods.UnbindMethod;
import org.apache.jackrabbit.webdav.property.DavProperty;
-import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -59,12 +79,17 @@
public class BindTest extends TestCase {
+ private String root;
private URI uri;
private String username, password;
private HttpClient client;
protected void setUp() throws Exception {
this.uri = URI.create(System.getProperty("webdav.test.url"));
+ this.root = this.uri.toASCIIString();
+ if (!this.root.endsWith("/")) {
+ this.root += "/";
+ }
this.username = System.getProperty(("webdav.test.username"), "");
this.password = System.getProperty(("webdav.test.password"), "");
this.client = new HttpClient();
@@ -84,33 +109,49 @@
int status = this.client.executeMethod(options);
assertEquals(200, status);
Set features = getDavFeatures(options);
+ List allow = Arrays.asList(options.getAllowedMethods());
assertTrue("DAV header should include 'bind' feature: " + features, features.contains("bind"));
+ assertTrue("Allow header should include BIND method", allow.contains("BIND"));
+ assertTrue("Allow header should include REBIND method", allow.contains("REBIND"));
+ assertTrue("Allow header should include UNBIND method", allow.contains("UNBIND"));
}
// create test resource, make it referenceable, check resource id, move resource, check again
public void testResourceId() throws HttpException, IOException, DavException, URISyntaxException {
- String testuri = this.uri.toASCIIString() + (this.uri.toASCIIString().endsWith("/") ? "" : "/") + "bindtest";
- String testuri2 = this.uri.toASCIIString() + (this.uri.toASCIIString().endsWith("/") ? "" : "/") + "bindtest2";
-
- PutMethod put = new PutMethod(testuri);
- put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
- int status = this.client.executeMethod(put);
- assertTrue(status == 200 || status == 201 || status == 204);
-
- // enabling version control always makes the resource referenceable
- VersionControlMethod versioncontrol = new VersionControlMethod(testuri);
- status = this.client.executeMethod(versioncontrol);
- assertTrue(status == 200 || status == 201);
-
- URI resourceId = getResourceId(testuri);
-
- MoveMethod move = new MoveMethod(testuri, testuri2, true);
- status = this.client.executeMethod(move);
- String s = move.getResponseBodyAsString();
- assertTrue(status == 204);
-
- URI resourceId2 = getResourceId(testuri2);
- assertEquals(resourceId, resourceId2);
+
+ String testcol = this.root + "testResourceId/";
+ String testuri1 = testcol + "bindtest1";
+ String testuri2 = testcol + "bindtest2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ PutMethod put = new PutMethod(testuri1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ // enabling version control always makes the resource referenceable
+ VersionControlMethod versioncontrol = new VersionControlMethod(testuri1);
+ status = this.client.executeMethod(versioncontrol);
+ assertTrue("status: " + status, status == 200 || status == 201);
+
+ URI resourceId = getResourceId(testuri1);
+
+ MoveMethod move = new MoveMethod(testuri1, testuri2, true);
+ status = this.client.executeMethod(move);
+ move.getResponseBodyAsString();
+ assertEquals(201, status);
+
+ URI resourceId2 = getResourceId(testuri2);
+ assertEquals(resourceId, resourceId2);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
}
// utility methods
@@ -118,14 +159,14 @@
// see http://greenbytes.de/tech/webdav/draft-ietf-webdav-bind-20.html#rfc.section.3.1
private URI getResourceId(String uri) throws IOException, DavException, URISyntaxException {
DavPropertyNameSet names = new DavPropertyNameSet();
- names.add(DavPropertyName.RESOURCEID);
+ names.add(BindConstants.RESOURCEID);
PropFindMethod propfind = new PropFindMethod(uri, names, 0);
int status = this.client.executeMethod(propfind);
- assertTrue(status == 207);
+ assertEquals(207, status);
MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus();
MultiStatusResponse[] responses = multistatus.getResponses();
assertEquals(1, responses.length);
- DavProperty resourceId = responses[0].getProperties(200).get(DavPropertyName.RESOURCEID);
+ DavProperty resourceId = responses[0].getProperties(200).get(BindConstants.RESOURCEID);
assertNotNull(resourceId);
assertTrue(resourceId.getValue() instanceof Element);
Element href = (Element)resourceId.getValue();
@@ -134,7 +175,472 @@
URI resid = new URI(text);
return resid;
}
-
+
+ private DavProperty getParentSet(String uri) throws IOException, DavException, URISyntaxException {
+ DavPropertyNameSet names = new DavPropertyNameSet();
+ names.add(BindConstants.PARENTSET);
+ PropFindMethod propfind = new PropFindMethod(uri, names, 0);
+ int status = this.client.executeMethod(propfind);
+ assertEquals(207, status);
+ MultiStatus multistatus = propfind.getResponseBodyAsMultiStatus();
+ MultiStatusResponse[] responses = multistatus.getResponses();
+ assertEquals(1, responses.length);
+ DavProperty parentset = responses[0].getProperties(200).get(BindConstants.PARENTSET);
+ assertNotNull(parentset);
+ return parentset;
+ }
+
+ public void testSimpleBind() throws Exception {
+ String testcol = this.root + "testSimpleBind/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //create new binding of R with path bindtest2/res2
+ DavMethodBase bind = new BindMethod(subcol2, new BindInfo(testres1, "res2"));
+ status = this.client.executeMethod(bind);
+ assertEquals(201, status);
+ //check if both bindings report the same DAV:resource-id
+ assertEquals(this.getResourceId(testres1), this.getResourceId(testres2));
+
+ //compare representations retrieved with both paths
+ GetMethod get = new GetMethod(testres1);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+ get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+
+ //modify R using the new path
+ put = new PutMethod(testres2);
+ put.setRequestEntity(new StringRequestEntity("bar", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertTrue("status: " + status, status == 200 || status == 204);
+
+ //compare representations retrieved with both paths
+ get = new GetMethod(testres1);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("bar", get.getResponseBodyAsString());
+ get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("bar", get.getResponseBodyAsString());
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testRebind() throws Exception {
+ String testcol = this.root + "testRebind/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ // enabling version control always makes the resource referenceable
+ VersionControlMethod versioncontrol = new VersionControlMethod(testres1);
+ status = this.client.executeMethod(versioncontrol);
+ assertTrue("status: " + status, status == 200 || status == 201);
+
+ URI r1 = this.getResourceId(testres1);
+
+ GetMethod get = new GetMethod(testres1);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+
+ //rebind R with path bindtest2/res2
+ DavMethodBase rebind = new RebindMethod(subcol2, new RebindInfo(testres1, "res2"));
+ status = this.client.executeMethod(rebind);
+ assertEquals(201, status);
+
+ URI r2 = this.getResourceId(testres2);
+
+ get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+
+ //make sure that rebind did not change the resource-id
+ assertEquals(r1, r2);
+
+ //verify that the initial binding is gone
+ HeadMethod head = new HeadMethod(testres1);
+ status = this.client.executeMethod(head);
+ assertEquals(404, status);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testBindOverwrite() throws Exception {
+ String testcol = this.root + "testSimpleBind/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //create new resource R' with path bindtest2/res2
+ put = new PutMethod(testres2);
+ put.setRequestEntity(new StringRequestEntity("bar", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //try to create new binding of R with path bindtest2/res2 and Overwrite:F
+ DavMethodBase bind = new BindMethod(subcol2, new BindInfo(testres1, "res2"));
+ bind.addRequestHeader(new Header("Overwrite", "F"));
+ status = this.client.executeMethod(bind);
+ assertEquals(412, status);
+
+ //verify that bindtest2/res2 still points to R'
+ GetMethod get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("bar", get.getResponseBodyAsString());
+
+ //create new binding of R with path bindtest2/res2
+ bind = new BindMethod(subcol2, new BindInfo(testres1, "res2"));
+ status = this.client.executeMethod(bind);
+ assertTrue("status: " + status, status == 200 || status == 204);
+
+ //verify that bindtest2/res2 now points to R
+ get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+
+ //verify that the initial binding is still there
+ HeadMethod head = new HeadMethod(testres1);
+ status = this.client.executeMethod(head);
+ assertEquals(200, status);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testRebindOverwrite() throws Exception {
+ String testcol = this.root + "testSimpleBind/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path testSimpleBind/bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ // enabling version control always makes the resource referenceable
+ VersionControlMethod versioncontrol = new VersionControlMethod(testres1);
+ status = this.client.executeMethod(versioncontrol);
+ assertTrue("status: " + status, status == 200 || status == 201);
+
+ //create new resource R' with path testSimpleBind/bindtest2/res2
+ put = new PutMethod(testres2);
+ put.setRequestEntity(new StringRequestEntity("bar", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //try rebind R with path testSimpleBind/bindtest2/res2 and Overwrite:F
+ RebindMethod rebind = new RebindMethod(subcol2, new RebindInfo(testres1, "res2"));
+ rebind.addRequestHeader(new Header("Overwrite", "F"));
+ status = this.client.executeMethod(rebind);
+ assertEquals(412, status);
+
+ //verify that testSimpleBind/bindtest2/res2 still points to R'
+ GetMethod get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("bar", get.getResponseBodyAsString());
+
+ //rebind R with path testSimpleBind/bindtest2/res2
+ rebind = new RebindMethod(subcol2, new RebindInfo(testres1, "res2"));
+ status = this.client.executeMethod(rebind);
+ assertTrue("status: " + status, status == 200 || status == 204);
+
+ //verify that testSimpleBind/bindtest2/res2 now points to R
+ get = new GetMethod(testres2);
+ status = this.client.executeMethod(get);
+ assertEquals(200, status);
+ assertEquals("foo", get.getResponseBodyAsString());
+
+ //verify that the initial binding is gone
+ HeadMethod head = new HeadMethod(testres1);
+ status = this.client.executeMethod(head);
+ assertEquals(404, status);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testParentSet() throws Exception {
+ String testcol = this.root + "testParentSet/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path testSimpleBind/bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //create new binding of R with path testSimpleBind/bindtest2/res2
+ DavMethodBase bind = new BindMethod(subcol2, new BindInfo(testres1, "res2"));
+ status = this.client.executeMethod(bind);
+ assertEquals(201, status);
+ //check if both bindings report the same DAV:resource-id
+ assertEquals(this.getResourceId(testres1), this.getResourceId(testres2));
+
+ //verify values of parent-set properties
+ List hrefs1 = new ArrayList();
+ List segments1 = new ArrayList();
+ List hrefs2 = new ArrayList();
+ List segments2 = new ArrayList();
+ Object ps1 = this.getParentSet(testres1).getValue();
+ Object ps2 = this.getParentSet(testres2).getValue();
+ assertTrue(ps1 instanceof List);
+ assertTrue(ps2 instanceof List);
+ List plist1 = (List) ps1;
+ List plist2 = (List) ps2;
+ assertEquals(2, plist1.size());
+ assertEquals(2, plist2.size());
+ for (int k = 0; k < 2; k++) {
+ Object pObj1 = plist1.get(k);
+ Object pObj2 = plist2.get(k);
+ assertTrue(pObj1 instanceof Element);
+ assertTrue(pObj2 instanceof Element);
+ ParentElement p1 = ParentElement.createFromXml((Element) pObj1);
+ ParentElement p2 = ParentElement.createFromXml((Element) pObj2);
+ hrefs1.add(p1.getHref());
+ hrefs2.add(p2.getHref());
+ segments1.add(p1.getSegment());
+ segments2.add(p2.getSegment());
+ }
+ Collections.sort(hrefs1);
+ Collections.sort(hrefs2);
+ Collections.sort(segments1);
+ Collections.sort(segments2);
+ assertEquals(hrefs1, hrefs2);
+ assertEquals(segments1, segments2);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testBindCollections() throws Exception {
+ String testcol = this.root + "testBindCollections/";
+ String a1 = testcol + "a1/";
+ String b1 = a1 + "b1/";
+ String c1 = b1 + "c1/";
+ String x1 = c1 + "x1";
+ String a2 = testcol + "a2/";
+ String b2 = a2 + "b2/";
+ String c2 = b2 + "c2/";
+ String x2 = c2 + "x2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(a1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(a2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create collection resource C
+ mkcol = new MkColMethod(b1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(c1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create plain resource R
+ PutMethod put = new PutMethod(x1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //create new binding of C with path a2/b2
+ DavMethodBase bind = new BindMethod(a2, new BindInfo(b1, "b2"));
+ status = this.client.executeMethod(bind);
+ assertEquals(201, status);
+ //check if both bindings report the same DAV:resource-id
+ assertEquals(this.getResourceId(b1), this.getResourceId(b2));
+
+ mkcol = new MkColMethod(c2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new binding of R with path a2/b2/c2/r2
+ bind = new BindMethod(c2, new BindInfo(x1, "x2"));
+ status = this.client.executeMethod(bind);
+ assertEquals(201, status);
+ //check if both bindings report the same DAV:resource-id
+ assertEquals(this.getResourceId(x1), this.getResourceId(x2));
+
+ //verify different path alternatives
+ URI rid = this.getResourceId(x1);
+ assertEquals(rid, this.getResourceId(x2));
+ assertEquals(rid, this.getResourceId(testcol + "a2/b2/c1/x1"));
+ assertEquals(rid, this.getResourceId(testcol + "a1/b1/c2/x2"));
+ Object ps = this.getParentSet(x1).getValue();
+ assertTrue(ps instanceof List);
+ assertEquals(2, ((List) ps).size());
+ ps = this.getParentSet(x2).getValue();
+ assertTrue(ps instanceof List);
+ assertEquals(2, ((List) ps).size());
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
+ public void testUnbind() throws Exception {
+ String testcol = this.root + "testUnbind/";
+ String subcol1 = testcol + "bindtest1/";
+ String testres1 = subcol1 + "res1";
+ String subcol2 = testcol + "bindtest2/";
+ String testres2 = subcol2 + "res2";
+ int status;
+ try {
+ MkColMethod mkcol = new MkColMethod(testcol);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol1);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+ mkcol = new MkColMethod(subcol2);
+ status = this.client.executeMethod(mkcol);
+ assertEquals(201, status);
+
+ //create new resource R with path testSimpleBind/bindtest1/res1
+ PutMethod put = new PutMethod(testres1);
+ put.setRequestEntity(new StringRequestEntity("foo", "text/plain", "UTF-8"));
+ status = this.client.executeMethod(put);
+ assertEquals(201, status);
+
+ //create new binding of R with path testSimpleBind/bindtest2/res2
+ DavMethodBase bind = new BindMethod(subcol2, new BindInfo(testres1, "res2"));
+ status = this.client.executeMethod(bind);
+ assertEquals(201, status);
+ //check if both bindings report the same DAV:resource-id
+ assertEquals(this.getResourceId(testres1), this.getResourceId(testres2));
+
+ //remove new path
+ UnbindMethod unbind = new UnbindMethod(subcol2, new UnbindInfo("res2"));
+ status = this.client.executeMethod(unbind);
+ assertTrue("status: " + status, status == 200 || status == 204);
+
+ //verify that the new binding is gone
+ HeadMethod head = new HeadMethod(testres2);
+ status = this.client.executeMethod(head);
+ assertEquals(404, status);
+
+ //verify that the initial binding is still there
+ head = new HeadMethod(testres1);
+ status = this.client.executeMethod(head);
+ assertEquals(200, status);
+ } finally {
+ DeleteMethod delete = new DeleteMethod(testcol);
+ status = this.client.executeMethod(delete);
+ assertTrue("status: " + status, status == 200 || status == 204);
+ }
+ }
+
private String getUri(Element href) {
String s = "";
for (Node c = href.getFirstChild(); c != null; c = c.getNextSibling()) {
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java (revision 700037)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequestImpl.java (working copy)
@@ -45,6 +45,9 @@
import org.apache.jackrabbit.webdav.version.report.ReportInfo;
import org.apache.jackrabbit.webdav.xml.DomUtil;
import org.apache.jackrabbit.webdav.xml.ElementIterator;
+import org.apache.jackrabbit.webdav.bind.RebindInfo;
+import org.apache.jackrabbit.webdav.bind.UnbindInfo;
+import org.apache.jackrabbit.webdav.bind.BindInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
@@ -189,6 +192,59 @@
}
/**
+ * Parse a href and return the path of the resource.
+ *
+ * @return path of the resource identified by the href.
+ * @see org.apache.jackrabbit.webdav.bind.BindServletRequest#getHrefLocator
+ */
+ public DavResourceLocator getHrefLocator(String href) throws DavException {
+ String ref = href;
+ if (ref != null) {
+ //href should be a Simple-ref production as defined in RFC4918, so it is either an absolute URI
+ //or an absoltute path
+ try {
+ URI uri = new URI(ref);
+ String auth = uri.getAuthority();
+ ref = uri.getRawPath();
+ if (auth == null) {
+ //verify that href is an absolute path
+ if (ref.startsWith("//") || !ref.startsWith("/")) {
+ log.warn("expected absolute path but found " + ref);
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else if (!auth.equals(httpRequest.getHeader("Host"))) {
+ //this looks like an unsupported cross-server operation, but of course a reverse-proxy
+ //might have rewritten the Host header. Since we can't find out, we have to reject anyway.
+ //Better use absolute paths in DAV:href elements!
+ throw new DavException(DavServletResponse.SC_FORBIDDEN);
+ }
+ } catch (URISyntaxException e) {
+ log.warn("malformed uri: " + href, e);
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ // cut off the context path
+ String contextPath = httpRequest.getContextPath();
+ if (ref.startsWith(contextPath)) {
+ ref = ref.substring(contextPath.length());
+ } else {
+ //absolute path has to start with contextpath
+ throw new DavException(DavServletResponse.SC_FORBIDDEN);
+ }
+ }
+ return factory.createResourceLocator(hrefPrefix, ref);
+ }
+
+ /**
+ * Returns the path of the member resource of the request resource which is identified by the segment parameter.
+ *
+ * @return path of internal member resource.
+ */
+ public DavResourceLocator getMemberLocator(String segment) {
+ String path = (this.getRequestLocator().getHref(true) + segment).substring(hrefPrefix.length());
+ return factory.createResourceLocator(hrefPrefix, path);
+ }
+
+ /**
* Return true if the overwrite header does not inhibit overwriting.
*
* @return true if the overwrite header requests 'overwriting'
@@ -726,6 +782,42 @@
return info;
}
+ /**
+ * @see org.apache.jackrabbit.webdav.bind.BindServletRequest#getRebindInfo()
+ */
+ public RebindInfo getRebindInfo() throws DavException {
+ RebindInfo info = null;
+ Document requestDocument = getRequestDocument();
+ if (requestDocument != null) {
+ info = RebindInfo.createFromXml(requestDocument.getDocumentElement());
+ }
+ return info;
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.bind.BindServletRequest#getUnbindInfo()
+ */
+ public UnbindInfo getUnbindInfo() throws DavException {
+ UnbindInfo info = null;
+ Document requestDocument = getRequestDocument();
+ if (requestDocument != null) {
+ info = UnbindInfo.createFromXml(requestDocument.getDocumentElement());
+ }
+ return info;
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.bind.BindServletRequest#getBindInfo()
+ */
+ public BindInfo getBindInfo() throws DavException {
+ BindInfo info = null;
+ Document requestDocument = getRequestDocument();
+ if (requestDocument != null) {
+ info = BindInfo.createFromXml(requestDocument.getDocumentElement());
+ }
+ return info;
+ }
+
//---------------------------------------< HttpServletRequest interface >---
public String getAuthType() {
return httpRequest.getAuthType();
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/property/DavPropertyName.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/property/DavPropertyName.java (revision 700037)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/property/DavPropertyName.java (working copy)
@@ -49,9 +49,6 @@
public static final DavPropertyName SOURCE = DavPropertyName.create(PROPERTY_SOURCE);
public static final DavPropertyName SUPPORTEDLOCK = DavPropertyName.create(PROPERTY_SUPPORTEDLOCK);
- /* webdav properties defined by the BIND specification */
- public static final DavPropertyName RESOURCEID = DavPropertyName.create(PROPERTY_RESOURCEID);
-
/* property use by microsoft that are not specified in the RFC 2518 */
public static final DavPropertyName ISCOLLECTION = DavPropertyName.create("iscollection");
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequest.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequest.java (revision 700037)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/WebdavRequest.java (working copy)
@@ -20,6 +20,7 @@
import org.apache.jackrabbit.webdav.ordering.OrderingDavServletRequest;
import org.apache.jackrabbit.webdav.transaction.TransactionDavServletRequest;
import org.apache.jackrabbit.webdav.version.DeltaVServletRequest;
+import org.apache.jackrabbit.webdav.bind.BindServletRequest;
/**
* The empty WebdavRequest interface collects the functionality
@@ -30,5 +31,6 @@
*/
public interface WebdavRequest extends DavServletRequest,
ObservationDavServletRequest, OrderingDavServletRequest,
- TransactionDavServletRequest, DeltaVServletRequest {
+ TransactionDavServletRequest, DeltaVServletRequest,
+ BindServletRequest {
}
\ No newline at end of file
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java (revision 700037)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/server/AbstractWebdavServlet.java (working copy)
@@ -32,6 +32,10 @@
import org.apache.jackrabbit.webdav.WebdavRequestImpl;
import org.apache.jackrabbit.webdav.WebdavResponse;
import org.apache.jackrabbit.webdav.WebdavResponseImpl;
+import org.apache.jackrabbit.webdav.bind.RebindInfo;
+import org.apache.jackrabbit.webdav.bind.UnbindInfo;
+import org.apache.jackrabbit.webdav.bind.BindableResource;
+import org.apache.jackrabbit.webdav.bind.BindInfo;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.InputContextImpl;
import org.apache.jackrabbit.webdav.io.OutputContext;
@@ -311,6 +315,15 @@
case DavMethods.DAV_ACL:
doAcl(request, response, resource);
break;
+ case DavMethods.DAV_REBIND:
+ doRebind(request, response, resource);
+ break;
+ case DavMethods.DAV_UNBIND:
+ doUnbind(request, response, resource);
+ break;
+ case DavMethods.DAV_BIND:
+ doBind(request, response, resource);
+ break;
default:
// any other method
return false;
@@ -575,7 +588,7 @@
}
DavResource destResource = getResourceFactory().createResource(request.getDestinationLocator(), request, response);
- int status = validateDestination(destResource, request);
+ int status = validateDestination(destResource, request, true);
if (status > DavServletResponse.SC_NO_CONTENT) {
response.sendError(status);
return;
@@ -598,7 +611,7 @@
DavResource resource) throws IOException, DavException {
DavResource destResource = getResourceFactory().createResource(request.getDestinationLocator(), request, response);
- int status = validateDestination(destResource, request);
+ int status = validateDestination(destResource, request, true);
if (status > DavServletResponse.SC_NO_CONTENT) {
response.sendError(status);
return;
@@ -609,6 +622,85 @@
}
/**
+ * The BIND method
+ *
+ * @param request
+ * @param response
+ * @param resource the collection resource to which a new member will be added
+ * @throws IOException
+ * @throws DavException
+ */
+ protected void doBind(WebdavRequest request, WebdavResponse response,
+ DavResource resource) throws IOException, DavException {
+
+ if (!resource.exists()) {
+ response.sendError(DavServletResponse.SC_NOT_FOUND);
+ }
+ BindInfo bindInfo = request.getBindInfo();
+ DavResource oldBinding = getResourceFactory().createResource(request.getHrefLocator(bindInfo.getHref()), request, response);
+ if (!(oldBinding instanceof BindableResource)) {
+ response.sendError(DavServletResponse.SC_METHOD_NOT_ALLOWED);
+ return;
+ }
+ DavResource newBinding = getResourceFactory().createResource(request.getMemberLocator(bindInfo.getSegment()), request, response);
+ int status = validateDestination(newBinding, request, false);
+ if (status > DavServletResponse.SC_NO_CONTENT) {
+ response.sendError(status);
+ return;
+ }
+ ((BindableResource) oldBinding).bind(resource, newBinding);
+ response.setStatus(status);
+ }
+
+ /**
+ * The REBIND method
+ *
+ * @param request
+ * @param response
+ * @param resource the collection resource to which a new member will be added
+ * @throws IOException
+ * @throws DavException
+ */
+ protected void doRebind(WebdavRequest request, WebdavResponse response,
+ DavResource resource) throws IOException, DavException {
+
+ if (!resource.exists()) {
+ response.sendError(DavServletResponse.SC_NOT_FOUND);
+ }
+ RebindInfo rebindInfo = request.getRebindInfo();
+ DavResource oldBinding = getResourceFactory().createResource(request.getHrefLocator(rebindInfo.getHref()), request, response);
+ if (!(oldBinding instanceof BindableResource)) {
+ response.sendError(DavServletResponse.SC_METHOD_NOT_ALLOWED);
+ return;
+ }
+ DavResource newBinding = getResourceFactory().createResource(request.getMemberLocator(rebindInfo.getSegment()), request, response);
+ int status = validateDestination(newBinding, request, false);
+ if (status > DavServletResponse.SC_NO_CONTENT) {
+ response.sendError(status);
+ return;
+ }
+ ((BindableResource) oldBinding).rebind(resource, newBinding);
+ response.setStatus(status);
+ }
+
+ /**
+ * The UNBIND method
+ *
+ * @param request
+ * @param response
+ * @param resource the collection resource from which a member will be removed
+ * @throws IOException
+ * @throws DavException
+ */
+ protected void doUnbind(WebdavRequest request, WebdavResponse response,
+ DavResource resource) throws IOException, DavException {
+
+ UnbindInfo unbindInfo = request.getUnbindInfo();
+ DavResource srcResource = getResourceFactory().createResource(request.getMemberLocator(unbindInfo.getSegment()), request, response);
+ resource.removeMember(srcResource);
+ }
+
+ /**
* Validate the given destination resource and return the proper status
* code: Any return value greater/equal than {@link DavServletResponse#SC_NO_CONTENT}
* indicates an error.
@@ -617,12 +709,14 @@
* @param request
* @return status code indicating whether the destination is valid.
*/
- private int validateDestination(DavResource destResource, WebdavRequest request)
+ private int validateDestination(DavResource destResource, WebdavRequest request, boolean checkHeader)
throws DavException {
- String destHeader = request.getHeader(HEADER_DESTINATION);
- if (destHeader == null || "".equals(destHeader)) {
- return DavServletResponse.SC_BAD_REQUEST;
+ if (checkHeader) {
+ String destHeader = request.getHeader(HEADER_DESTINATION);
+ if (destHeader == null || "".equals(destHeader)) {
+ return DavServletResponse.SC_BAD_REQUEST;
+ }
}
if (destResource.getLocator().equals(request.getRequestLocator())) {
return DavServletResponse.SC_FORBIDDEN;
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/RebindInfo.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/RebindInfo.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/RebindInfo.java (revision 0)
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.xml.ElementIterator;
+import org.apache.jackrabbit.webdav.xml.XmlSerializable;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+
+public class RebindInfo implements XmlSerializable {
+
+ private static Logger log = LoggerFactory.getLogger(RebindInfo.class);
+
+ private String segment;
+ private String href;
+
+ public RebindInfo(String href, String segment) {
+ this.href = href;
+ this.segment = segment;
+ }
+
+ public String getHref() {
+ return this.href;
+ }
+
+ public String getSegment() {
+ return this.segment;
+ }
+
+ /**
+ * Build an RebindInfo object from the root element present
+ * in the request body.
+ *
+ * @param root the root element of the request body
+ * @return a RebindInfo object containing segment and href
+ * @throws org.apache.jackrabbit.webdav.DavException if the REBIND request is malformed
+ */
+ public static RebindInfo createFromXml(Element root) throws DavException {
+ if (!DomUtil.matches(root, BindConstants.XML_REBIND, BindConstants.NAMESPACE)) {
+ log.warn("DAV:rebind element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ String href = null;
+ String segment = null;
+ ElementIterator it = DomUtil.getChildren(root);
+ while (it.hasNext()) {
+ Element elt = it.nextElement();
+ if (DomUtil.matches(elt, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE)) {
+ if (segment == null) {
+ segment = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:segment element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else if (DomUtil.matches(elt, BindConstants.XML_HREF, BindConstants.NAMESPACE)) {
+ if (href == null) {
+ href = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:href element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else {
+ log.warn("unexpected element " + elt.getLocalName());
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ }
+ if (href == null) {
+ log.warn("DAV:href element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ if (segment == null) {
+ log.warn("DAV:segment element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ return new RebindInfo(href, segment);
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.xml.XmlSerializable#toXml(org.w3c.dom.Document)
+ */
+ public Element toXml(Document document) {
+ Element rebindElt = DomUtil.createElement(document, BindConstants.XML_REBIND, BindConstants.NAMESPACE);
+ Element hrefElt = DomUtil.createElement(document, BindConstants.XML_HREF, BindConstants.NAMESPACE, this.href);
+ Element segElt = DomUtil.createElement(document, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE, this.segment);
+ rebindElt.appendChild(hrefElt);
+ rebindElt.appendChild(segElt);
+ return rebindElt;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentElement.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentElement.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentElement.java (revision 0)
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.apache.jackrabbit.webdav.xml.XmlSerializable;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.xml.ElementIterator;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ParentElement wraps en element of the parent set of a resource. A java.util.Set of
+ * ParentElement objects may serve as the value object of the ParentSet DavProperty.
+ */
+public class ParentElement implements XmlSerializable {
+
+ private static Logger log = LoggerFactory.getLogger(ParentElement.class);
+
+ private final String href;
+ private final String segment;
+
+ public ParentElement(String href, String segment) {
+ this.href = href;
+ this.segment = segment;
+ }
+
+ public String getHref() {
+ return this.href;
+ }
+
+ public String getSegment() {
+ return this.segment;
+ }
+
+ /**
+ * Build an ParentElement object from an XML element DAV:parent
+ *
+ * @param root the DAV:parent element
+ * @return a ParentElement object
+ * @throws org.apache.jackrabbit.webdav.DavException if the DAV:parent element is malformed
+ */
+ public static ParentElement createFromXml(Element root) throws DavException {
+ if (!DomUtil.matches(root, BindConstants.XML_PARENT, BindConstants.NAMESPACE)) {
+ log.warn("DAV:paret element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ String href = null;
+ String segment = null;
+ ElementIterator it = DomUtil.getChildren(root);
+ while (it.hasNext()) {
+ Element elt = it.nextElement();
+ if (DomUtil.matches(elt, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE)) {
+ if (segment == null) {
+ segment = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:segment element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else if (DomUtil.matches(elt, BindConstants.XML_HREF, BindConstants.NAMESPACE)) {
+ if (href == null) {
+ href = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:href element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else {
+ log.warn("unexpected element " + elt.getLocalName());
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ }
+ if (href == null) {
+ log.warn("DAV:href element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ if (segment == null) {
+ log.warn("DAV:segment element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ return new ParentElement(href, segment);
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.xml.XmlSerializable#toXml(org.w3c.dom.Document)
+ */
+ public Element toXml(Document document) {
+ Element parentElt = DomUtil.createElement(document, BindConstants.XML_PARENT, BindConstants.NAMESPACE);
+ Element hrefElt = DomUtil.createElement(document, BindConstants.XML_HREF, BindConstants.NAMESPACE, this.href);
+ Element segElt = DomUtil.createElement(document, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE, this.segment);
+ parentElt.appendChild(hrefElt);
+ parentElt.appendChild(segElt);
+ return parentElt;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentSet.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentSet.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/ParentSet.java (revision 0)
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.apache.jackrabbit.webdav.xml.XmlSerializable;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.property.AbstractDavProperty;
+import org.apache.jackrabbit.webdav.property.DavPropertyName;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * ParentSet represents a DAV:parent-set property.
+ */
+public class ParentSet extends AbstractDavProperty {
+
+ private final Collection parents;
+
+ /**
+ * Creates a new ParentSet from a collection of ParentElement objects.
+ * @param parents
+ */
+ public ParentSet(Collection parents) {
+ super(BindConstants.PARENTSET, true);
+ this.parents = parents;
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.property.AbstractDavProperty#getValue()
+ */
+ public Object getValue() {
+ return this.parents;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindInfo.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindInfo.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindInfo.java (revision 0)
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.xml.ElementIterator;
+import org.apache.jackrabbit.webdav.xml.XmlSerializable;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+
+public class BindInfo implements XmlSerializable {
+
+ private static Logger log = LoggerFactory.getLogger(BindInfo.class);
+
+ private String segment;
+ private String href;
+
+ public BindInfo(String href, String segment) {
+ this.href = href;
+ this.segment = segment;
+ }
+
+ public String getHref() {
+ return this.href;
+ }
+
+ public String getSegment() {
+ return this.segment;
+ }
+
+ /**
+ * Build an BindInfo object from the root element present
+ * in the request body.
+ *
+ * @param root the root element of the request body
+ * @return a BindInfo object containing segment and href
+ * @throws org.apache.jackrabbit.webdav.DavException if the BIND request is malformed
+ */
+ public static BindInfo createFromXml(Element root) throws DavException {
+ if (!DomUtil.matches(root, BindConstants.XML_BIND, BindConstants.NAMESPACE)) {
+ log.warn("DAV:bind element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ String href = null;
+ String segment = null;
+ ElementIterator it = DomUtil.getChildren(root);
+ while (it.hasNext()) {
+ Element elt = it.nextElement();
+ if (DomUtil.matches(elt, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE)) {
+ if (segment == null) {
+ segment = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:segment element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else if (DomUtil.matches(elt, BindConstants.XML_HREF, BindConstants.NAMESPACE)) {
+ if (href == null) {
+ href = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:href element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else {
+ log.warn("unexpected element " + elt.getLocalName());
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ }
+ if (href == null) {
+ log.warn("DAV:href element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ if (segment == null) {
+ log.warn("DAV:segment element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ return new BindInfo(href, segment);
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.xml.XmlSerializable#toXml(org.w3c.dom.Document)
+ */
+ public Element toXml(Document document) {
+ Element bindElt = DomUtil.createElement(document, BindConstants.XML_BIND, BindConstants.NAMESPACE);
+ Element hrefElt = DomUtil.createElement(document, BindConstants.XML_HREF, BindConstants.NAMESPACE, this.href);
+ Element segElt = DomUtil.createElement(document, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE, this.segment);
+ bindElt.appendChild(hrefElt);
+ bindElt.appendChild(segElt);
+ return bindElt;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindableResource.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindableResource.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindableResource.java (revision 0)
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.DavResource;
+
+import java.util.Collection;
+import java.util.Set;
+
+public interface BindableResource {
+
+ /**
+ * Will add a new binding to the given collection referencing this resource.
+ *
+ * @param collection the collection to create the new binding in.
+ * @param newBinding the new binding
+ */
+ public void bind(DavResource collection, DavResource newBinding) throws DavException;
+
+ /**
+ * Will rebind the resource to the given collection. By definition, this is an atomic move operation.
+ *
+ * @param collection the collection to create the new binding in.
+ * @param newBinding the new binding
+ */
+ public void rebind(DavResource collection, DavResource newBinding) throws DavException;
+
+ /**
+ * Will retrieve a collection of parent elements of the bindable resource representing the parent set.
+ *
+ * @return newBinding the new binding
+ */
+ public Set getParentElements();
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindConstants.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindConstants.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindConstants.java (revision 0)
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.apache.jackrabbit.webdav.xml.Namespace;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.property.DavPropertyName;
+
+/**
+ * BindConstants provide constants for request and response
+ * headers, Xml elements and property names defined by
+ * the BIND specification.
+ */
+public interface BindConstants {
+
+ /**
+ * The namespace
+ */
+ public static final Namespace NAMESPACE = DavConstants.NAMESPACE;
+
+ /**
+ * local names of XML elements used in the request bodies of the methods BIND, REBIND and UNBIND.
+ */
+ public static final String XML_BIND = "bind";
+ public static final String XML_REBIND = "rebind";
+ public static final String XML_UNBIND = "unbind";
+ public static final String XML_SEGMENT = "segment";
+ public static final String XML_HREF = "href";
+ public static final String XML_PARENT = "parent";
+
+ public static final String METHODS = "BIND, REBIND, UNBIND";
+
+ public static final String COMPLIANCE_CLASS = "bind";
+
+ /*
+ * Webdav properties defined by the BIND specification.
+ */
+ public static final DavPropertyName RESOURCEID = DavPropertyName.create("resource-id");
+ public static final DavPropertyName PARENTSET = DavPropertyName.create("parent-set");
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindServletRequest.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindServletRequest.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/BindServletRequest.java (revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.DavResourceLocator;
+
+/**
+ * BindServletRequest provides extension useful for functionality
+ * related to BIND specification.
+ */
+public interface BindServletRequest {
+
+ /**
+ * Returns the {@link RebindInfo} present with the request
+ *
+ * @return {@link RebindInfo} object
+ * @throws org.apache.jackrabbit.webdav.DavException in case of an invalid or missing request body
+ */
+ public RebindInfo getRebindInfo() throws DavException;
+
+ /**
+ * Returns the {@link UnbindInfo} present with the request
+ *
+ * @return {@link UnbindInfo} object
+ * @throws org.apache.jackrabbit.webdav.DavException in case of an invalid or missing request body
+ */
+ public UnbindInfo getUnbindInfo() throws DavException;
+
+ /**
+ * Returns the {@link BindInfo} present with the request
+ *
+ * @return {@link BindInfo} object
+ * @throws org.apache.jackrabbit.webdav.DavException in case of an invalid or missing request body
+ */
+ public BindInfo getBindInfo() throws DavException;
+
+ /**
+ * Parses a href and returns the path of the resource.
+ *
+ * @return path of the resource identified by the href.
+ */
+ public DavResourceLocator getHrefLocator(String href) throws DavException;
+
+ /**
+ * Returns the path of the member resource of the request resource which is identified by the segment parameter.
+ *
+ * @return path of internal member resource.
+ */
+ public DavResourceLocator getMemberLocator(String segment);
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/UnbindInfo.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/UnbindInfo.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/bind/UnbindInfo.java (revision 0)
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.bind;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+import org.apache.jackrabbit.webdav.DavException;
+import org.apache.jackrabbit.webdav.xml.DomUtil;
+import org.apache.jackrabbit.webdav.xml.ElementIterator;
+import org.apache.jackrabbit.webdav.xml.XmlSerializable;
+import org.w3c.dom.Element;
+import org.w3c.dom.Document;
+
+public class UnbindInfo implements XmlSerializable {
+
+ private static Logger log = LoggerFactory.getLogger(UnbindInfo.class);
+
+ private String segment;
+
+ private UnbindInfo() {}
+
+ public UnbindInfo(String segment) {
+ this.segment = segment;
+ }
+
+ public String getSegment() {
+ return this.segment;
+ }
+
+ /**
+ * Build an UnbindInfo object from the root element present
+ * in the request body.
+ *
+ * @param root the root element of the request body
+ * @return a UnbindInfo object containing a segment identifier
+ * @throws org.apache.jackrabbit.webdav.DavException if the UNBIND request is malformed
+ */
+ public static UnbindInfo createFromXml(Element root) throws DavException {
+ if (!DomUtil.matches(root, BindConstants.XML_UNBIND, BindConstants.NAMESPACE)) {
+ log.warn("DAV:unbind element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ String segment = null;
+ ElementIterator it = DomUtil.getChildren(root);
+ while (it.hasNext()) {
+ Element elt = it.nextElement();
+ if (DomUtil.matches(elt, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE)) {
+ if (segment == null) {
+ segment = DomUtil.getText(elt);
+ } else {
+ log.warn("unexpected multiple occurence of DAV:segment element");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ } else {
+ log.warn("unexpected element " + elt.getLocalName());
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ }
+ if (segment == null) {
+ log.warn("DAV:segment element expected");
+ throw new DavException(DavServletResponse.SC_BAD_REQUEST);
+ }
+ return new UnbindInfo(segment);
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.xml.XmlSerializable#toXml(org.w3c.dom.Document)
+ */
+ public Element toXml(Document document) {
+ Element unbindElt = DomUtil.createElement(document, BindConstants.XML_UNBIND, BindConstants.NAMESPACE);
+ Element segElt = DomUtil.createElement(document, BindConstants.XML_SEGMENT, BindConstants.NAMESPACE, this.segment);
+ unbindElt.appendChild(segElt);
+ return unbindElt;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/UnbindMethod.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/UnbindMethod.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/UnbindMethod.java (revision 0)
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.client.methods;
+
+import org.apache.jackrabbit.webdav.bind.UnbindInfo;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+
+import java.io.IOException;
+
+/**
+ * UnbindMethod removes a binding to a resource (semantically equivalent to delete).
+ */
+public class UnbindMethod extends DavMethodBase {
+
+ public UnbindMethod(String uri, UnbindInfo info) throws IOException {
+ super(uri);
+ setRequestBody(info);
+ }
+
+ //---------------------------------------------------------< HttpMethod >---
+ /**
+ * @see org.apache.commons.httpclient.HttpMethod#getName()
+ */
+ public String getName() {
+ return "UNBIND";
+ }
+
+ //------------------------------------------------------< DavMethodBase >---
+ /**
+ *
+ * @param statusCode
+ * @return true if status code is 200 (binding was removed).
+ */
+ protected boolean isSuccess(int statusCode) {
+ return statusCode == DavServletResponse.SC_OK;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/RebindMethod.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/RebindMethod.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/RebindMethod.java (revision 0)
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.client.methods;
+
+import org.apache.jackrabbit.webdav.bind.RebindInfo;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+
+import java.io.IOException;
+
+/**
+ * RebindMethod replaces a binding to a resource (atomic version of move).
+ */
+public class RebindMethod extends DavMethodBase {
+
+ public RebindMethod(String uri, RebindInfo info) throws IOException {
+ super(uri);
+ setRequestBody(info);
+ }
+
+ //---------------------------------------------------------< HttpMethod >---
+ /**
+ * @see org.apache.commons.httpclient.HttpMethod#getName()
+ */
+ public String getName() {
+ return "REBIND";
+ }
+
+ //------------------------------------------------------< DavMethodBase >---
+ /**
+ *
+ * @param statusCode
+ * @return true if status code is 200 (existing binding was overwritten) or 201 (new binding created).
+ */
+ protected boolean isSuccess(int statusCode) {
+ return statusCode == DavServletResponse.SC_CREATED || statusCode == DavServletResponse.SC_OK;
+ }
+}
+
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BindMethod.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BindMethod.java (revision 0)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/client/methods/BindMethod.java (revision 0)
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.jackrabbit.webdav.client.methods;
+
+import org.apache.jackrabbit.webdav.bind.BindInfo;
+import org.apache.jackrabbit.webdav.DavServletResponse;
+
+import java.io.IOException;
+
+/**
+ * BindMethod creates a new binding to a resource.
+ */
+public class BindMethod extends DavMethodBase {
+
+ public BindMethod(String uri, BindInfo info) throws IOException {
+ super(uri);
+ setRequestBody(info);
+ }
+
+ //---------------------------------------------------------< HttpMethod >---
+ /**
+ * @see org.apache.commons.httpclient.HttpMethod#getName()
+ */
+ public String getName() {
+ return "BIND";
+ }
+
+ //------------------------------------------------------< DavMethodBase >---
+ /**
+ *
+ * @param statusCode
+ * @return true if status code is 200 (existing binding was overwritten) or 201 (new binding created).
+ */
+ protected boolean isSuccess(int statusCode) {
+ return statusCode == DavServletResponse.SC_CREATED || statusCode == DavServletResponse.SC_OK;
+ }
+}
Index: jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavMethods.java
===================================================================
--- jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavMethods.java (revision 700037)
+++ jackrabbit-webdav/src/main/java/org/apache/jackrabbit/webdav/DavMethods.java (working copy)
@@ -262,6 +262,27 @@
public static final String METHOD_ACL = "ACL";
/**
+ * The webdav REBIND method and public constant defined by
+ * the BIND specification
+ */
+ public static final int DAV_REBIND = DAV_ACL + 1;
+ public static final String METHOD_REBIND = "REBIND";
+
+ /**
+ * The webdav UNBIND method and public constant defined by
+ * the BIND specification
+ */
+ public static final int DAV_UNBIND = DAV_REBIND + 1;
+ public static final String METHOD_UNBIND = "UNBIND";
+
+ /**
+ * The webdav BIND method and public constant defined by
+ * the BIND specification
+ */
+ public static final int DAV_BIND = DAV_UNBIND + 1;
+ public static final String METHOD_BIND = "BIND";
+
+ /**
* Returns webdav method type code, error result <= 0
* Valid type codes > 0
*/
@@ -314,6 +335,9 @@
addMethodCode(METHOD_BASELINE_CONTROL, DAV_BASELINE_CONTROL);
addMethodCode(METHOD_MKACTIVITY, DAV_MKACTIVITY);
addMethodCode(METHOD_ACL, DAV_ACL);
+ addMethodCode(METHOD_REBIND, DAV_REBIND);
+ addMethodCode(METHOD_UNBIND, DAV_UNBIND);
+ addMethodCode(METHOD_BIND, DAV_BIND);
labelMethods = new int[] { DAV_GET, DAV_HEAD, DAV_OPTIONS, DAV_PROPFIND,
DAV_LABEL, DAV_COPY };
Index: jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java
===================================================================
--- jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java (revision 700037)
+++ jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java (working copy)
@@ -39,6 +39,10 @@
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
+import org.apache.jackrabbit.webdav.bind.BindConstants;
+import org.apache.jackrabbit.webdav.bind.BindableResource;
+import org.apache.jackrabbit.webdav.bind.ParentSet;
+import org.apache.jackrabbit.webdav.bind.ParentElement;
import org.apache.jackrabbit.webdav.io.InputContext;
import org.apache.jackrabbit.webdav.io.OutputContext;
import org.apache.jackrabbit.webdav.jcr.JcrDavException;
@@ -69,6 +73,7 @@
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import javax.jcr.Workspace;
import javax.jcr.lock.Lock;
import java.io.IOException;
import java.io.OutputStream;
@@ -78,17 +83,22 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
/**
* DavResourceImpl implements a DavResource.
*/
-public class DavResourceImpl implements DavResource, JcrConstants {
+public class DavResourceImpl implements DavResource, BindableResource, JcrConstants {
/**
* the default logger
*/
private static final Logger log = LoggerFactory.getLogger(DavResourceImpl.class);
+ public static final String METHODS = DavResource.METHODS + ", " + BindConstants.METHODS;
+ public static final String COMPLIANCE_CLASS = DavResource.COMPLIANCE_CLASS + ", " + BindConstants.COMPLIANCE_CLASS;
+
private DavResourceFactory factory;
private LockManager lockManager;
private JcrDavSession session;
@@ -196,7 +206,7 @@
*/
private void initRfc4122Uri() {
try {
- if (node.isNodeType("mix:referenceable")) {
+ if (node.isNodeType(MIX_REFERENCEABLE)) {
String uuid = node.getUUID();
try {
UUID.fromString(uuid);
@@ -215,7 +225,7 @@
* @see org.apache.jackrabbit.webdav.DavResource#getComplianceClass()
*/
public String getComplianceClass() {
- return DavResource.COMPLIANCE_CLASS;
+ return COMPLIANCE_CLASS;
}
/**
@@ -223,7 +233,7 @@
* @see org.apache.jackrabbit.webdav.DavResource#getSupportedMethods()
*/
public String getSupportedMethods() {
- return DavResource.METHODS;
+ return METHODS;
}
/**
@@ -369,9 +379,14 @@
}
if (rfc4122Uri != null) {
- properties.add(new HrefProperty(DavPropertyName.RESOURCEID, rfc4122Uri, true));
+ properties.add(new HrefProperty(BindConstants.RESOURCEID, rfc4122Uri, true));
}
+ Set parentElements = this.getParentElements();
+ if (!parentElements.isEmpty()) {
+ properties.add(new ParentSet(parentElements));
+ }
+
/* set current lock information. If no lock is set to this resource,
an empty lockdiscovery will be returned in the response. */
properties.add(new LockDiscovery(getLock(Type.WRITE, Scope.EXCLUSIVE)));
@@ -612,7 +627,13 @@
try {
String itemPath = member.getLocator().getRepositoryPath();
Item memItem = getJcrSession().getItem(itemPath);
- memItem.remove();
+ //TODO once jcr2 is out: simply call removeShare()
+ if (memItem instanceof org.apache.jackrabbit.api.jsr283.Node) {
+ org.apache.jackrabbit.api.jsr283.Node n = (org.apache.jackrabbit.api.jsr283.Node) memItem;
+ n.removeShare();
+ } else {
+ memItem.remove();
+ }
getJcrSession().save();
// make sure, non-jcr locks are removed, once the removal is completed
@@ -836,7 +857,99 @@
return session;
}
+
/**
+ * @see BindableResource#rebind(DavResource, DavResource)
+ */
+ public void bind(DavResource collection, DavResource newBinding) throws DavException {
+ if (!exists()) {
+ //DAV:bind-source-exists
+ throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
+ }
+ if (isLocked(collection)) {
+ //DAV:locked-update-allowed?
+ throw new DavException(DavServletResponse.SC_LOCKED);
+ }
+ if (isFilteredResource(newBinding)) {
+ throw new DavException(DavServletResponse.SC_FORBIDDEN);
+ }
+ checkSameWorkspace(collection.getLocator());
+ try {
+ if (!this.node.isNodeType(MIX_SHAREABLE)) {
+ if (!this.node.canAddMixin(MIX_SHAREABLE)) {
+ //DAV:binding-allowed
+ throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
+ }
+ this.node.addMixin(MIX_SHAREABLE);
+ this.node.save();
+ }
+ Workspace workspace = this.session.getRepositorySession().getWorkspace();
+ workspace.clone(workspace.getName(), this.node.getPath(), newBinding.getLocator().getRepositoryPath(), false);
+
+ } catch (RepositoryException e) {
+ throw new JcrDavException(e);
+ }
+
+ }
+
+ /**
+ * @see BindableResource#rebind(DavResource, DavResource)
+ */
+ public void rebind(DavResource collection, DavResource newBinding) throws DavException {
+ if (!exists()) {
+ //DAV:rebind-source-exists
+ throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
+ }
+ if (isLocked(this)) {
+ //DAV:protected-source-url-deletion.allowed
+ throw new DavException(DavServletResponse.SC_PRECONDITION_FAILED);
+ }
+ if (isLocked(collection)) {
+ //DAV:locked-update-allowed?
+ throw new DavException(DavServletResponse.SC_LOCKED);
+ }
+ if (isFilteredResource(newBinding)) {
+ throw new DavException(DavServletResponse.SC_FORBIDDEN);
+ }
+ checkSameWorkspace(collection.getLocator());
+ try {
+ if (!this.node.isNodeType(MIX_REFERENCEABLE)) {
+ throw new DavException(this.node.canAddMixin(MIX_REFERENCEABLE)?
+ DavServletResponse.SC_CONFLICT : DavServletResponse.SC_METHOD_NOT_ALLOWED);
+ }
+ getJcrSession().getWorkspace().move(locator.getRepositoryPath(), newBinding.getLocator().getRepositoryPath());
+ } catch (RepositoryException e) {
+ throw new JcrDavException(e);
+ }
+ }
+
+ /**
+ * @see org.apache.jackrabbit.webdav.bind.BindableResource#getParentElements()
+ */
+ public Set getParentElements() {
+ try {
+ //TODO remove this check once jcr2 is out
+ if (!(this.node instanceof org.apache.jackrabbit.api.jsr283.Node)) {
+ DavResourceLocator loc = this.locator.getFactory().createResourceLocator(
+ this.locator.getPrefix(), this.locator.getWorkspacePath(), this.node.getParent().getPath(), false);
+ return Collections.singleton(new ParentElement(loc.getHref(true), this.node.getName()));
+ }
+ Set ps = new HashSet();
+ NodeIterator sharedSetIterator = ((org.apache.jackrabbit.api.jsr283.Node) this.node).getSharedSet();
+ while (sharedSetIterator.hasNext()) {
+ Node sharednode = sharedSetIterator.nextNode();
+ DavResourceLocator loc = this.locator.getFactory().createResourceLocator(
+ this.locator.getPrefix(), this.locator.getWorkspacePath(), sharednode.getParent().getPath(), false);
+ ps.add(new ParentElement(loc.getHref(true), sharednode.getName()));
+ }
+ return ps;
+ } catch (RepositoryException e) {
+ log.warn("unable to calculate parent set", e);
+ return Collections.EMPTY_SET;
+ }
+ }
+
+ /**
* Returns the node that is wrapped by this resource.
*
* @return
Index: jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DeltaVResourceImpl.java
===================================================================
--- jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DeltaVResourceImpl.java (revision 700037)
+++ jackrabbit-jcr-server/src/main/java/org/apache/jackrabbit/webdav/simple/DeltaVResourceImpl.java (working copy)
@@ -32,6 +32,7 @@
import org.apache.jackrabbit.webdav.DavServletResponse;
import org.apache.jackrabbit.webdav.DavSession;
import org.apache.jackrabbit.webdav.DavCompliance;
+import org.apache.jackrabbit.webdav.bind.BindConstants;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.HrefProperty;
@@ -77,7 +78,8 @@
DavCompliance._2_,
DavCompliance.VERSION_CONTROL,
DavCompliance.VERSION_HISTORY,
- DavCompliance.LABEL
+ DavCompliance.LABEL,
+ BindConstants.COMPLIANCE_CLASS,
});
}
Index: jackrabbit-jcr-server/pom.xml
===================================================================
--- jackrabbit-jcr-server/pom.xml (revision 700037)
+++ jackrabbit-jcr-server/pom.xml (working copy)
@@ -66,7 +66,12 @@
jcr
+
org.apache.jackrabbit
+ jackrabbit-api
+
+
+ org.apache.jackrabbit
jackrabbit-jcr-commons