Added: 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteServerIT.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteServerIT.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteServerIT.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteServerIT.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,1361 @@
+/*
+ * 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.oak.remote.http.handler;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+import com.mashape.unirest.http.HttpResponse;
+import com.mashape.unirest.http.JsonNode;
+import org.apache.jackrabbit.oak.NodeStoreFixture;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.OakBaseTest;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.jcr.Jcr;
+import org.apache.jackrabbit.oak.remote.RemoteRepository;
+import org.apache.jackrabbit.oak.remote.content.ContentRemoteRepository;
+import org.apache.jackrabbit.util.ISO8601;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.jcr.SimpleCredentials;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.mashape.unirest.http.Unirest.get;
+import static com.mashape.unirest.http.Unirest.head;
+import static com.mashape.unirest.http.Unirest.patch;
+import static com.mashape.unirest.http.Unirest.post;
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class RemoteServerIT extends OakBaseTest {
+
+    private ContentRepository contentRepository;
+
+    private ContentSession contentSession;
+
+    private RemoteRepository remoteRepository;
+
+    private RemoteServer remoteServer;
+
+    public RemoteServerIT(NodeStoreFixture fixture) {
+        super(fixture);
+    }
+
+    private InputStream asStream(String s) {
+        return new ByteArrayInputStream(s.getBytes());
+    }
+
+    private String asString(Calendar calendar) {
+        return ISO8601.format(calendar);
+    }
+
+    private RemoteServer getRemoteServer(RemoteRepository repository, String 
host, int port) {
+        return new RemoteServer(repository, host, port);
+    }
+
+    private RemoteRepository getRemoteRepository(ContentRepository repository) 
{
+        return new ContentRemoteRepository(repository);
+    }
+
+    private ContentRepository getContentRepository() {
+        Oak oak = new Oak(store);
+
+        new Jcr(oak);
+
+        return oak.createContentRepository();
+    }
+
+    private ContentSession getContentSession(ContentRepository repository) 
throws Exception {
+        return repository.login(new SimpleCredentials("admin", 
"admin".toCharArray()), null);
+    }
+
+    private String getHost() {
+        return "localhost";
+    }
+
+    private int getPort() {
+        return 28080;
+    }
+
+    private String resource(String path) {
+        return "http://"; + getHost() + ":" + getPort() + path;
+    }
+
+    private String load(String name) throws Exception {
+        return Files.toString(new 
File(getClass().getResource(name).getFile()), Charsets.UTF_8);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        contentRepository = getContentRepository();
+        contentSession = getContentSession(contentRepository);
+        remoteRepository = getRemoteRepository(contentRepository);
+        remoteServer = getRemoteServer(remoteRepository, getHost(), getPort());
+        remoteServer.start();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        remoteServer.stop();
+        contentSession.close();
+    }
+
+    @Test
+    public void testReadLastRevision() throws Exception {
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last")).basicAuth("admin", "admin").asJson();
+        assertEquals(200, response.getStatus());
+
+        JSONObject payload = response.getBody().getObject();
+        assertNotNull(payload.getString("revision"));
+    }
+
+    @Test
+    public void testReadLastRevisionWithoutAuthentication() throws Exception {
+        assertEquals(401, 
get(resource("/revisions/last")).asJson().getStatus());
+    }
+
+    @Test
+    public void testReadLastRevisionTree() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        assertEquals(200, response.getStatus());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWithoutAuthentication() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).asJson();
+        assertEquals(401, response.getStatus());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeRevision() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        assertFalse(response.getHeaders().getFirst("oak-revision").isEmpty());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWithNotExistingTree() throws Exception 
{
+        assertEquals(404, 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getStatus());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeHasMoreChildren() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertFalse(body.getBoolean("hasMoreChildren"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeChildren() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree child = node.addChild("child");
+        child.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeStringProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "a", Type.STRING);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("string", property.getString("type"));
+        assertEquals("a", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiStringProperty() throws Exception 
{
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("a", "b"), Type.STRINGS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("strings", property.getString("type"));
+        assertEquals("a", property.getJSONArray("value").getString(0));
+        assertEquals("b", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeBinaryProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(root.createBlob(asStream("a")), 
root.createBlob(asStream("b"))), Type.BINARIES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("binaryIds", property.getString("type"));
+        assertFalse(property.getJSONArray("value").getString(0).isEmpty());
+        assertFalse(property.getJSONArray("value").getString(1).isEmpty());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiBinaryProperty() throws Exception 
{
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", root.createBlob(asStream("a")), 
Type.BINARY);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("binaryId", property.getString("type"));
+        assertFalse(property.getString("value").isEmpty());
+    }
+
+    @Test
+    public void testReadLastRevisionTreeLongProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", 42L, Type.LONG);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("long", property.getString("type"));
+        assertEquals(42L, property.getLong("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiLongProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(4L, 2L), Type.LONGS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("longs", property.getString("type"));
+        assertEquals(4L, property.getJSONArray("value").getLong(0));
+        assertEquals(2L, property.getJSONArray("value").getLong(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeDoubleProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", 4.2, Type.DOUBLE);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("double", property.getString("type"));
+        assertEquals(4.2, property.getDouble("value"), 1e-10);
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiDoubleProperty() throws Exception 
{
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(4.2, 2.4), Type.DOUBLES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("doubles", property.getString("type"));
+        assertEquals(4.2, property.getJSONArray("value").getDouble(0), 1e-10);
+        assertEquals(2.4, property.getJSONArray("value").getDouble(1), 1e-10);
+    }
+
+    @Test
+    public void testReadLastRevisionTreeDateProperty() throws Exception {
+        Calendar calendar = Calendar.getInstance();
+
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asString(calendar), Type.DATE);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("date", property.getString("type"));
+        assertEquals(calendar.getTimeInMillis(), property.getLong("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiDateProperty() throws Exception {
+        Calendar calendar = Calendar.getInstance();
+
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(asString(calendar), 
asString(calendar)), Type.DATES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("dates", property.getString("type"));
+        assertEquals(calendar.getTimeInMillis(), 
property.getJSONArray("value").getLong(0));
+        assertEquals(calendar.getTimeInMillis(), 
property.getJSONArray("value").getLong(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeBooleanProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", true, Type.BOOLEAN);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("boolean", property.getString("type"));
+        assertEquals(true, property.getBoolean("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiBooleanProperty() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(true, false), Type.BOOLEANS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("booleans", property.getString("type"));
+        assertEquals(true, property.getJSONArray("value").getBoolean(0));
+        assertEquals(false, property.getJSONArray("value").getBoolean(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeNameProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "value", Type.NAME);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("name", property.getString("type"));
+        assertEquals("value", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiNameProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("first", "second"), Type.NAMES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("names", property.getString("type"));
+        assertEquals("first", property.getJSONArray("value").getString(0));
+        assertEquals("second", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreePathProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "/value", Type.PATH);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("path", property.getString("type"));
+        assertEquals("/value", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiPathProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("/first", "/second"), Type.PATHS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("paths", property.getString("type"));
+        assertEquals("/first", property.getJSONArray("value").getString(0));
+        assertEquals("/second", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeReferenceProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "value", Type.REFERENCE);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("reference", property.getString("type"));
+        assertEquals("value", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiReferenceProperty() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("first", "second"), 
Type.REFERENCES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("references", property.getString("type"));
+        assertEquals("first", property.getJSONArray("value").getString(0));
+        assertEquals("second", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWeakReferenceProperty() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "value", Type.WEAKREFERENCE);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("weakReference", property.getString("type"));
+        assertEquals("value", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiWeakReferenceProperty() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("first", "second"), 
Type.WEAKREFERENCES);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("weakReferences", property.getString("type"));
+        assertEquals("first", property.getJSONArray("value").getString(0));
+        assertEquals("second", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeUriProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", "http://acme.org";, Type.URI);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("uri", property.getString("type"));
+        assertEquals("http://acme.org";, property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiUriProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList("http://acme.org";, 
"http://acme.com";), Type.URIS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("uris", property.getString("type"));
+        assertEquals("http://acme.org";, 
property.getJSONArray("value").getString(0));
+        assertEquals("http://acme.com";, 
property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeDecimalProperty() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", BigDecimal.ZERO, Type.DECIMAL);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("decimal", property.getString("type"));
+        assertEquals("0", property.getString("value"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeMultiDecimalProperty() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("property", asList(BigDecimal.ZERO, BigDecimal.ONE), 
Type.DECIMALS);
+
+        root.commit();
+
+        JSONObject body = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getBody().getObject();
+        assertTrue(body.getJSONObject("children").isNull("child"));
+
+        JSONObject property = 
body.getJSONObject("properties").getJSONObject("property");
+        assertEquals("decimals", property.getString("type"));
+        assertEquals("0", property.getJSONArray("value").getString(0));
+        assertEquals("1", property.getJSONArray("value").getString(1));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWithDepth() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree child = node.addChild("child");
+        child.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree grandChild = child.addChild("grandChild");
+        grandChild.setProperty("jcr:primaryType", "nt:unstructured", 
Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").queryString("depth", 1).asJson();
+        assertEquals(200, response.getStatus());
+
+        JSONObject body = response.getBody().getObject();
+        assertFalse(body.getJSONObject("children").isNull("child"));
+        
assertTrue(body.getJSONObject("children").getJSONObject("child").getJSONObject("children").isNull("grandChild"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWithWithPropertyFilters() throws 
Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("foo", "foo", Type.STRING);
+        node.setProperty("bar", "bar", Type.STRING);
+        node.setProperty("baz", "baz", Type.STRING);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").queryString("properties", "ba*").queryString("properties", 
"-baz").asJson();
+        assertEquals(200, response.getStatus());
+
+        JSONObject body = response.getBody().getObject();
+        assertFalse(body.getJSONObject("properties").has("foo"));
+        assertTrue(body.getJSONObject("properties").has("bar"));
+        assertFalse(body.getJSONObject("properties").has("baz"));
+    }
+
+    @Test
+    public void testReadLastRevisionTreeWithWithNodeFilters() throws Exception 
{
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree foo = node.addChild("foo");
+        foo.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree bar = node.addChild("bar");
+        bar.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree baz = node.addChild("baz");
+        baz.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").queryString("children", "ba*").queryString("children", 
"-baz").asJson();
+        assertEquals(200, response.getStatus());
+
+        JSONObject body = response.getBody().getObject();
+        assertFalse(body.getJSONObject("children").has("foo"));
+        assertTrue(body.getJSONObject("children").has("bar"));
+        assertFalse(body.getJSONObject("children").has("baz"));
+    }
+
+    @Test
+    public void testReadTreeAtRevision() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        String revision = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", 
"admin").asJson().getHeaders().getFirst("oak-revision");
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/{revision}/tree/node")).basicAuth("admin", 
"admin").routeParam("revision", revision).asJson();
+        assertEquals(200, response.getStatus());
+        assertEquals(revision, response.getHeaders().getFirst("oak-revision"));
+    }
+
+    @Test
+    public void testReadTreeAtRevisionWithInvalidRevision() throws Exception {
+        assertEquals(410, 
get(resource("/revisions/any/tree/node")).basicAuth("admin", 
"admin").asJson().getStatus());
+    }
+
+    @Test
+    public void testReadBinary() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("test")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}")).basicAuth("admin", 
"admin").routeParam("binaryId", binaryId).asString();
+        assertEquals(200, binaryResponse.getStatus());
+        assertEquals("test", binaryResponse.getBody());
+        assertEquals("4", 
binaryResponse.getHeaders().getFirst("content-length"));
+    }
+
+    @Test
+    public void testReadBinaryWithoutAuthentication() throws Exception {
+        HttpResponse<String> response = 
get(resource("/binaries/any")).asString();
+        assertEquals(401, response.getStatus());
+    }
+
+    @Test
+    public void testReadBinaryWithInvalidId() throws Exception {
+        HttpResponse<String> response = 
get(resource("/binaries/any")).basicAuth("admin", "admin").asString();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void testReadBinaryRangeOffsetOnly() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        // Offset = 0
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*0\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("10", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("0123456789", binaryResponse.getBody());
+
+        // Offset = 3
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes = 3")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*3\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("7", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("3456789", binaryResponse.getBody());
+
+        // Offset = 9 (last)
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=9")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*9\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("1", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("9", binaryResponse.getBody());
+    }
+
+    @Test
+    public void testReadBinaryRangeSuffix() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        // Last 10 bytes (full body)
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=-10")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*0\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("10", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("0123456789", binaryResponse.getBody());
+
+        // Last 3 bytes
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=-3")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*7\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("3", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("789", binaryResponse.getBody());
+
+        // Last 1 byte
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=-1")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*9\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("1", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("9", binaryResponse.getBody());
+    }
+
+    @Test
+    public void testReadBinarySingleRange() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        // Range 0-9 (full body)
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0-9")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*0\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("10", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("0123456789", binaryResponse.getBody());
+
+        // Range 0- (full body)
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0-")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*0\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("10", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("0123456789", binaryResponse.getBody());
+
+        // Range 3-6
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=3- 6")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*3\\s*\\-\\s*6\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("4", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("3456", binaryResponse.getBody());
+
+        // Range 9-9
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes= 9 - 9")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+        assertTrue(binaryResponse.getHeaders().containsKey("content-range"));
+        
assertTrue(binaryResponse.getHeaders().getFirst("content-range").matches("^\\s*9\\s*\\-\\s*9\\s*/\\s*(10|\\*)\\s*$"));
+        assertEquals("1", 
binaryResponse.getHeaders().getFirst("content-length"));
+        assertEquals("9", binaryResponse.getBody());
+    }
+
+    @Test
+    public void testReadBinaryMultipleRanges() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0,-1 , 2-2, 3-6")
+                .asString();
+        assertEquals(206, binaryResponse.getStatus());
+
+        String contentType = 
binaryResponse.getHeaders().getFirst("content-type");
+        assertNotNull(contentType);
+
+        Pattern multipartDelimiterPattern = 
Pattern.compile("^\\s*multipart/byteranges;\\s*boundary=(.+)");
+        Matcher matcher = multipartDelimiterPattern.matcher(contentType);
+        assertTrue(matcher.matches());
+
+        //TODO: Validate response body
+    }
+
+    @Test
+    public void testReadBinaryInvalidRanges() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        // Unknown range unit = elephant
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "elephant=0-9")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+
+        // Invalid range header
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "this is not correct")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+
+        // Missing range unit
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "0")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+
+        // Range limit greater than file size
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0-1000")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+
+        // Range start greater than end
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=9-8")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+
+        // One bad range will "break" all ranges
+        binaryResponse = get(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .header("Range", "bytes=0, -1, 10000")
+                .asString();
+        assertNotEquals(206, binaryResponse.getStatus());
+        assertNotEquals(500, binaryResponse.getStatus());
+    }
+
+    @Test
+    public void testExistsBinary() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree node = root.getTree("/").addChild("node");
+        node.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+        node.setProperty("binary", root.createBlob(asStream("0123456789")), 
Type.BINARY);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = 
get(resource("/revisions/last/tree/node")).basicAuth("admin", "admin").asJson();
+        String binaryId = 
response.getBody().getObject().getJSONObject("properties").getJSONObject("binary").getString("value");
+
+        // Offset = 0
+        HttpResponse<String> binaryResponse = 
head(resource("/binaries/{binaryId}"))
+                .basicAuth("admin", "admin")
+                .routeParam("binaryId", binaryId)
+                .asString();
+        assertEquals(200, binaryResponse.getStatus());
+        assertEquals("bytes", 
binaryResponse.getHeaders().getFirst("accept-ranges"));
+    }
+
+    @Test
+    public void testExistsBinaryWithInvalidId() throws Exception {
+        HttpResponse<String> response = 
head(resource("/binaries/any")).basicAuth("admin", "admin").asString();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void testExistsBinaryWithoutAuthentication() throws Exception {
+        HttpResponse<String> response = 
head(resource("/binaries/any")).asString();
+        assertEquals(401, response.getStatus());
+    }
+
+    @Test
+    public void testCreateBinary() throws Exception {
+        HttpResponse<JsonNode> response = 
post(resource("/binaries")).basicAuth("admin", "admin").body("body").asJson();
+        assertEquals(201, response.getStatus());
+
+        String binaryId = response.getBody().getObject().getString("binaryId");
+        assertFalse(binaryId.isEmpty());
+
+        HttpResponse<String> binaryResponse = 
get(resource("/binaries/{binaryId}")).basicAuth("admin", 
"admin").routeParam("binaryId", binaryId).asString();
+        assertEquals(200, binaryResponse.getStatus());
+        assertEquals("body", binaryResponse.getBody());
+    }
+
+    @Test
+    public void testCreateBinaryWithoutAuthentication() throws Exception {
+        assertEquals(401, 
post(resource("/binaries")).body("body").asJson().getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeBinaryProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeBinaryProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeMultiBinaryProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiBinaryProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeBooleanProperty() throws Exception 
{
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeBooleanProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeMultiBooleanProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiBooleanProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeDateProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeDateProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNodeMultiDateProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiDateProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddDecimalProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeDecimalProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiDecimalProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiDecimalProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddDoubleProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeDoubleProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiDoubleProperty() throws Exception 
{
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiDoubleProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddLongProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeLongProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiLongProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiLongProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddNameProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeNameProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiNameProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiNameProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddPathProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodePathProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiPathProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiPathProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddReferenceProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeReferenceProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiReferenceProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiReferenceProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddStringProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeStringProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiStringProperty() throws Exception 
{
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiStringProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddUriProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeUriProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiUriProperty() throws Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiUriProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddWeakReferenceProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeWeakReferenceProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testPatchLastRevisionAddMultiWeakReferenceProperty() throws 
Exception {
+        HttpResponse<JsonNode> response = 
patch(resource("/revisions/last/tree")).basicAuth("admin", 
"admin").body(load("addNodeMultiWeakReferenceProperty.json")).asJson();
+        assertEquals(201, response.getStatus());
+    }
+
+    @Test
+    public void testSearchLastRevision() throws Exception {
+        Root root = contentSession.getLatestRoot();
+
+        Tree test = root.getTree("/").addChild("test");
+        test.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree n1 = test.addChild("one");
+        n1.setProperty("name", "one", Type.STRING);
+        n1.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        Tree n2 = test.addChild("two");
+        n2.setProperty("name", "two", Type.STRING);
+        n2.setProperty("jcr:primaryType", "nt:unstructured", Type.NAME);
+
+        root.commit();
+
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .basicAuth("admin", "admin")
+                .queryString("query", "select name from nt:unstructured as 
node where jcr:path like '/test/%'")
+                .queryString("language", "sql")
+                .queryString("offset", 0)
+                .queryString("limit", 10)
+                .asJson();
+
+        assertEquals(200, response.getStatus());
+
+        JSONObject results = response.getBody().getObject();
+        assertNotNull(results.getLong("total"));
+
+        List<String> columns = getStringArray(results, "columns");
+        assertTrue(columns.contains("name"));
+        assertTrue(columns.contains("jcr:path"));
+
+        List<String> selectors = getStringArray(results, "selectors");
+        assertTrue(selectors.contains("node"));
+    }
+
+    private List<String> getStringArray(JSONObject parent, String name) {
+        List<String> result = newArrayList();
+
+        JSONArray array = parent.getJSONArray(name);
+
+        for (int i = 0; i < array.length(); i++) {
+            result.add(array.getString(i));
+        }
+
+        return result;
+    }
+
+    @Test
+    public void testSearchLastRevisionWithoutAuthentication() throws Exception 
{
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .queryString("query", "select name from nt:unstructured as 
node where jcr:path like '/test/%'")
+                .queryString("language", "sql")
+                .queryString("offset", 0)
+                .queryString("limit", 10)
+                .asJson();
+
+        assertEquals(401, response.getStatus());
+    }
+
+    @Test
+    public void testSearchLastRevisionWithoutQuery() throws Exception {
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .basicAuth("admin", "admin")
+                .queryString("language", "sql")
+                .queryString("offset", 0)
+                .queryString("limit", 10)
+                .asJson();
+
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSearchLastRevisionWithoutLanguage() throws Exception {
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .basicAuth("admin", "admin")
+                .queryString("query", "select name from nt:unstructured as 
node where jcr:path like '/test/%'")
+                .queryString("offset", 0)
+                .queryString("limit", 10)
+                .asJson();
+
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSearchLastRevisionWithoutOffset() throws Exception {
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .basicAuth("admin", "admin")
+                .queryString("query", "select name from nt:unstructured as 
node where jcr:path like '/test/%'")
+                .queryString("language", "sql")
+                .queryString("limit", 10)
+                .asJson();
+
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void testSearchLastRevisionWithoutLimit() throws Exception {
+        HttpResponse<JsonNode> response = get(resource("/revisions/last/tree"))
+                .basicAuth("admin", "admin")
+                .queryString("query", "select name from nt:unstructured as 
node where jcr:path like '/test/%'")
+                .queryString("language", "sql")
+                .queryString("offset", 0)
+                .asJson();
+
+        assertEquals(400, response.getStatus());
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcherTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcherTest.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcherTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcherTest.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,68 @@
+/*
+ * 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.oak.remote.http.matcher;
+
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class AllMatcherTest {
+
+    boolean matches(boolean... results) {
+        Matcher[] matchers = new Matcher[results.length];
+
+        for (int i = 0; i < results.length; i++) {
+            matchers[i] = mock(Matcher.class);
+        }
+
+        HttpServletRequest request = mock(HttpServletRequest.class);
+
+        for (int i = 0; i < results.length; i++) {
+            doReturn(results[i]).when(matchers[i]).match(request);
+        }
+
+        return new AllMatcher(matchers).match(request);
+
+    }
+
+    @Test
+    public void testNoMatchers() {
+        assertTrue(matches());
+    }
+
+    @Test
+    public void testSingleMatcher() {
+        assertFalse(matches(false));
+        assertTrue(matches(true));
+    }
+
+    @Test
+    public void testMatchers() {
+        assertFalse(matches(false, false));
+        assertFalse(matches(false, true));
+        assertFalse(matches(true, false));
+        assertTrue(matches(true, true));
+    }
+
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MatchersTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MatchersTest.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MatchersTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MatchersTest.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,85 @@
+/*
+ * 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.oak.remote.http.matcher;
+
+import org.junit.Test;
+
+import static 
org.apache.jackrabbit.oak.remote.http.matcher.Matchers.matchesAll;
+import static 
org.apache.jackrabbit.oak.remote.http.matcher.Matchers.matchesMethod;
+import static 
org.apache.jackrabbit.oak.remote.http.matcher.Matchers.matchesPath;
+import static 
org.apache.jackrabbit.oak.remote.http.matcher.Matchers.matchesRequest;
+import static org.junit.Assert.assertNotNull;
+
+public class MatchersTest {
+
+    @Test
+    public void testCreateMethodMatcher() {
+        assertNotNull(matchesMethod("GET"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateMethodMatcherWithNullMethod() {
+        matchesMethod(null);
+    }
+
+    @Test
+    public void testCreatePathMatcher() {
+        assertNotNull(matchesPath("/test"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreatePathMatcherWithNullPattern() {
+        matchesPath(null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreatePathMatcherWithInvalidPattern() {
+        matchesPath("/test(");
+    }
+
+    @Test
+    public void testCreateAllMatcher() {
+        assertNotNull(matchesAll());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateAllMatchersWithNullMatcher() {
+        matchesAll(null, null);
+    }
+
+    @Test
+    public void testCreateRequestMatcher() {
+        assertNotNull(matchesRequest("GET", "/test"));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRequestMatcherWithNullMethod() {
+        matchesRequest(null, "/test");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRequestMatcherWithNullPattern() {
+        matchesRequest("GET", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateRequestMatcherWithInvalidPattern() {
+        matchesRequest("GET", "/test(");
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcherTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcherTest.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcherTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/MethodMatcherTest.java
 Thu Jun 11 12:09:15 2015
@@ -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.oak.remote.http.matcher;
+
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class MethodMatcherTest {
+
+    private boolean matches(String requestMethod, String matcherMethod) {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        doReturn(requestMethod).when(request).getMethod();
+        return new MethodMatcher(matcherMethod).match(request);
+    }
+
+    @Test
+    public void testMatch() {
+        assertTrue(matches("GET", "GET"));
+    }
+
+    @Test
+    public void testNoMatch() {
+        assertFalse(matches("GET", "PUT"));
+    }
+
+    @Test
+    public void testCaseInsensitiveMatch() {
+        assertTrue(matches("GET", "get"));
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcherTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcherTest.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcherTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/java/org/apache/jackrabbit/oak/remote/http/matcher/PathMatcherTest.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,58 @@
+/*
+ * 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.oak.remote.http.matcher;
+
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+public class PathMatcherTest {
+
+    private boolean matches(String requestPath, String matcherPattern) {
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        doReturn(requestPath).when(request).getPathInfo();
+        return new PathMatcher(Pattern.compile(matcherPattern)).match(request);
+    }
+
+    @Test
+    public void testMatch() {
+        assertTrue(matches("/test", "/test"));
+    }
+
+    @Test
+    public void testCaseSensitiveMatch() {
+        assertFalse(matches("/test", "/Test"));
+    }
+
+    @Test
+    public void testPatternMatch() {
+        assertTrue(matches("/test/something", "/test/.*"));
+    }
+
+    @Test
+    public void testNoPathInfo() {
+        assertFalse(matches(null, "/test"));
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBinaryProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBinaryProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBinaryProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBinaryProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "binary",
+        "value": "dGVzdA=="
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBooleanProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBooleanProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBooleanProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeBooleanProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "boolean",
+        "value": true
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDateProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDateProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDateProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDateProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "date",
+        "value": 1426849486095
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDecimalProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDecimalProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDecimalProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDecimalProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "decimal",
+        "value": "1.23e3"
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDoubleProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDoubleProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDoubleProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeDoubleProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "double",
+        "value": 1.23
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeLongProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeLongProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeLongProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeLongProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,16 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "long",
+        "value": 123
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBinaryProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBinaryProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBinaryProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBinaryProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "binaries",
+        "value": [
+          "dGVzdA==",
+          "dGVzdA=="
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBooleanProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBooleanProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBooleanProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiBooleanProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "booleans",
+        "value": [
+          true,
+          false
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDateProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDateProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDateProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDateProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "dates",
+        "value": [
+          1426849486095,
+          1426849486095
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDecimalProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDecimalProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDecimalProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDecimalProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "decimals",
+        "value": [
+          "1.23e3",
+          "4.56e4"
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDoubleProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDoubleProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDoubleProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiDoubleProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "doubles",
+        "value": [
+          1.23,
+          4.56
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiLongProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiLongProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiLongProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiLongProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "longs",
+        "value": [
+          123,
+          456
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiNameProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiNameProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiNameProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiNameProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "names",
+        "value": [
+          "one",
+          "two"
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiPathProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiPathProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiPathProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiPathProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "paths",
+        "value": [
+          "/one",
+          "/two"
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiReferenceProperty.json
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiReferenceProperty.json?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiReferenceProperty.json
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/test/resources/org/apache/jackrabbit/oak/remote/http/handler/addNodeMultiReferenceProperty.json
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,19 @@
+[
+  {
+    "op": "add",
+    "path": "/test",
+    "properties": {
+      "jcr:primaryType": {
+        "type": "name",
+        "value": "nt:unstructured"
+      },
+      "property": {
+        "type": "references",
+        "value": [
+          "one",
+          "two"
+        ]
+      }
+    }
+  }
+]
\ No newline at end of file


Reply via email to