You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2011/12/08 19:18:45 UTC

svn commit: r1212018 [4/7] - in /jackrabbit/sandbox/jackrabbit-microkernel: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/jackrabbit/ src/main/java/org/apache/jackrabbit/configuration/ src/main/j...

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java Thu Dec  8 18:18:42 2011
@@ -0,0 +1,186 @@
+/*
+ * 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.utils;
+
+import org.apache.jackrabbit.spi.commons.iterator.Iterators;
+
+import javax.jcr.RangeIterator;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Adapter for turning a {@link java.util.Iterator} into a {@link javax.jcr.RangeIterator}.
+ */
+public class RangeIteratorAdapter<T> implements RangeIterator {
+
+    /**
+     * Static instance of an empty {@link RangeIterator}.
+     */
+    public static final RangeIterator EMPTY = new RangeIteratorAdapter<Object>(Iterators.empty());
+
+    /**
+     * The adapted iterator instance.
+     */
+    private final Iterator<T> iterator;
+
+    /**
+     * Number of elements in the adapted iterator, or -1 if unknown.
+     */
+    private long size;
+
+    /**
+     * Current position of the iterator.
+     */
+    private long position;
+
+    /**
+     * Creates an adapter for the given iterator of the given size.
+     *
+     * @param iterator adapted iterator
+     * @param size size of the iterator, or -1 if unknown
+     */
+    public RangeIteratorAdapter(Iterator<T> iterator, long size) {
+        this.iterator = iterator;
+        this.size = size;
+        position = 0;
+    }
+
+    /**
+     * Creates an adapter for the given iterator of unknown size.
+     *
+     * @param iterator adapted iterator
+     */
+    public RangeIteratorAdapter(Iterator<T> iterator) {
+        this(iterator, -1);
+    }
+
+    /**
+     * Creates a {@link RangeIterator} for the given collection.
+     *
+     * @param collection the collection to iterate
+     */
+    public RangeIteratorAdapter(Collection<T> collection) {
+        this(collection.iterator(), collection.size());
+    }
+
+    //-------------------------------------------------------< RangeIterator >
+
+    /**
+     * Returns the current position of the iterator.
+     *
+     * @return iterator position
+     */
+    @Override
+    public long getPosition() {
+        return position;
+    }
+
+    /**
+     * Returns the size of the iterator.
+     *
+     * @return iterator size, or -1 if unknown
+     */
+    @Override
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * Skips the given number of elements.
+     *
+     * @param n number of elements to skip
+     * @throws IllegalArgumentException if n is negative
+     * @throws java.util.NoSuchElementException if skipped past the last element
+     */
+    @Override
+    public void skip(long n) throws IllegalArgumentException, NoSuchElementException {
+        if (n < 0) {
+            throw new IllegalArgumentException("skip(" + n + ')');
+        }
+
+        for (long i = 0; i < n; i++) {
+            next();
+        }
+    }
+
+    //------------------------------------------------------------< Iterator >
+
+    /**
+     * Checks if this iterator has more elements. If there are no more
+     * elements and the size of the iterator is unknown, then the size is
+     * set to the current position.
+     *
+     * @return {@code true} if this iterator has more elements,
+     *         {@code false} otherwise
+     */
+    @Override
+    public boolean hasNext() {
+        if (iterator.hasNext()) {
+            return true;
+        }
+        else {
+            if (size == -1) {
+                size = position;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Returns the next element in this iterator and advances the iterator
+     * position. If there are no more elements and the size of the iterator
+     * is unknown, then the size is set to the current position.
+     *
+     * @return next element
+     * @throws NoSuchElementException if there are no more elements
+     */
+    @Override
+    public T next() throws NoSuchElementException {
+        try {
+            T next = iterator.next();
+            position++;
+            return next;
+        }
+        catch (NoSuchElementException e) {
+            if (size == -1) {
+                size = position;
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * Removes the previously retrieved element. Decreases the current
+     * position and size of this iterator.
+     *
+     * @throws UnsupportedOperationException if removes are not permitted
+     * @throws IllegalStateException if there is no previous element to remove
+     */
+    @Override
+    public void remove() throws UnsupportedOperationException, IllegalStateException {
+        iterator.remove();
+        position--;
+        if (size != -1) {
+            size--;
+        }
+    }
+
+}

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/RangeIteratorAdapter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java Thu Dec  8 18:18:42 2011
@@ -0,0 +1,29 @@
+/*
+ * 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.utils;
+
+public final class Unchecked {
+    private Unchecked() {}
+
+    @SuppressWarnings("unchecked")
+    public static <T> T cast(Object o) {
+        return (T) o;
+    }
+}

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java Thu Dec  8 18:18:42 2011
@@ -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.utils;
+
+import org.apache.jackrabbit.json.JsonValue;
+import org.apache.jackrabbit.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.json.JsonValue.Type;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ValueConverter {
+    private ValueConverter() {}
+
+    public static JsonValue toJsonValue(Value value) throws RepositoryException {
+        switch (value.getType()) {
+            case PropertyType.STRING: {
+                return new JsonAtom(value.getString(), Type.STRING);
+            }
+            case PropertyType.LONG:
+            case PropertyType.DOUBLE:
+            case PropertyType.DECIMAL: {
+                return new JsonAtom(value.getString(), Type.NUMBER);  // todo mind NaN
+            }
+            case PropertyType.BOOLEAN: {
+                return new JsonAtom(value.getString(), Type.BOOLEAN);
+            }
+            case PropertyType.BINARY:
+            case PropertyType.DATE:
+            case PropertyType.NAME:
+            case PropertyType.PATH:
+            case PropertyType.REFERENCE:
+            case PropertyType.WEAKREFERENCE:
+            case PropertyType.URI:
+            default: {
+                throw new UnsupportedRepositoryOperationException("toJson"); // todo implement toJson
+            }
+        }
+    }
+
+    public static JsonArray toJsonValue(Value[] values) throws RepositoryException {
+        List<JsonValue> jsonValues = new ArrayList<JsonValue>();
+        for (Value value : values) {
+            if (value != null) {
+                jsonValues.add(toJsonValue(value));
+            }
+        }
+        return new JsonArray(jsonValues);
+    }
+
+    public static Value toValue(ValueFactory valueFactory, JsonAtom jsonAtom) {
+        switch (jsonAtom.type()) {
+            case STRING: {
+                return valueFactory.createValue(jsonAtom.value());  // todo decode to correct type
+            }
+            case NUMBER: {
+                String value = jsonAtom.value();
+                try {
+                    return valueFactory.createValue(Long.valueOf(value));
+                }
+                catch (NumberFormatException e) {
+                    try {
+                        return valueFactory.createValue(Double.valueOf(value));
+                    }
+                    catch (NumberFormatException e1) {
+                        return valueFactory.createValue(new BigDecimal(value));
+                    }
+                }
+            }
+            case BOOLEAN: {
+                return valueFactory.createValue(Boolean.valueOf(jsonAtom.value()));
+            }
+            default:
+                throw new IllegalArgumentException(jsonAtom.toString());
+        }
+    }
+
+    public static Value[] toValue(ValueFactory valueFactory, JsonArray jsonArray) {
+        List<JsonValue> jsonValues = jsonArray.value();
+        Value[] values = new Value[jsonValues.size()];
+        int k = 0;
+        for (JsonValue jsonValue : jsonValues) {
+            values[k++] = toValue(valueFactory, jsonValue.asAtom());
+        }
+        return values;
+    }
+}

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/main/resources/META-INF/services/javax.jcr.RepositoryFactory Thu Dec  8 18:18:42 2011
@@ -0,0 +1,17 @@
+#  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.
+org.apache.jackrabbit.RepositoryFactoryImpl
+
+

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java Thu Dec  8 18:18:42 2011
@@ -0,0 +1,1581 @@
+/*
+ * 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;
+
+import org.apache.jackrabbit.configuration.RepositoryConfiguration;
+import org.apache.jackrabbit.util.Loop;
+import org.apache.jackrabbit.util.NumberStream;
+import org.apache.jackrabbit.utils.Arrays;
+import org.apache.jackrabbit.mk.MicroKernelFactory;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import javax.imageio.spi.ServiceRegistry;
+import javax.jcr.Binary;
+import javax.jcr.GuestCredentials;
+import javax.jcr.Item;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.NodeTypeTemplate;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.observation.ObservationManager;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static java.util.Arrays.asList;
+import static org.apache.jackrabbit.utils.Arrays.contains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class RepositoryTest {
+    private static final String URL = "fs:target/repository-test/repository";
+    // private static final String URL = "mem:";
+
+    private static final String TEST_NODE = "test_node";
+    private static final String TEST_PATH = '/' + TEST_NODE;
+    private Repository repository;
+    private Session session;
+
+    @Before
+    public void setup() {
+        MicroKernel mk = MicroKernelFactory.getInstance(URL);
+        try {
+            String head = mk.getHeadRevision();
+            if (mk.nodeExists("/default", head)) {
+                head = mk.commit("/", "- \"default\"", head, null);
+            }
+
+            head = mk.commit("/",
+                    "+\"default\" : {" +
+                            "\"foo\" : {" +
+                            "\"stringProp\":\"stringVal\"," +
+                            "\"intProp\":42," +
+                            "\"mvProp\":[1,2,3]}, " +
+                            "\"bar\" : {}}", head, "{}");
+
+            mk.commit("/default",
+                    "+\"" + TEST_NODE + "\" : {\"jcr:primaryType\":\"7:nt:unstructured\"}", head, "{}");
+        }
+        finally {
+            mk.dispose();
+        }
+    }
+
+    @After
+    public void tearDown() {
+        if (session != null) {
+            session.logout();
+            session = null;
+        }
+    }
+
+    @Test
+    public void createRepository() throws RepositoryException {
+        Repository repository = getRepository();
+        assertNotNull(repository);
+    }
+
+    @Test
+    public void login() throws RepositoryException {
+        assertNotNull(getSession());
+    }
+
+    @Test(expected = NoSuchWorkspaceException.class)
+    public void loginInvalidWorkspace() throws RepositoryException {
+        Repository repository = getRepository();
+        repository.login("invalid");
+    }
+
+    @Test
+    public void getWorkspaceNames() throws RepositoryException {
+        String[] workspaces = getSession().getWorkspace().getAccessibleWorkspaceNames();
+
+        Set<String> names = new HashSet<String>() {{
+            add("default");
+        }};
+
+        assertTrue(asList(workspaces).containsAll(names));
+        assertTrue(names.containsAll(asList(workspaces)));
+    }
+
+    @Test
+    public void createDeleteWorkspace() throws RepositoryException {
+        getSession().getWorkspace().createWorkspace("new");
+
+        Session session2 = getRepository().login();
+        try {
+            String[] workspaces = session2.getWorkspace().getAccessibleWorkspaceNames();
+            assertTrue(asList(workspaces).contains("new"));
+            Session session3 = getRepository().login("new");
+            assertEquals("new", session3.getWorkspace().getName());
+            session3.logout();
+            session2.getWorkspace().deleteWorkspace("new");
+        }
+        finally {
+            session2.logout();
+        }
+
+        Session session4 = getRepository().login();
+        try {
+            String[] workspaces = session4.getWorkspace().getAccessibleWorkspaceNames();
+            assertFalse(asList(workspaces).contains("new"));
+        }
+        finally {
+            session4.logout();
+        }
+    }
+
+    @Test
+    public void getRoot() throws RepositoryException {
+        Node root = getSession().getRootNode();
+        assertNotNull(root);
+        assertEquals("", root.getName());
+        assertEquals("/", root.getPath());
+    }
+
+    @Test
+    public void getRootFromPath() throws RepositoryException {
+        Node root = getNode("/");
+        assertEquals("", root.getName());
+        assertEquals("/", root.getPath());
+    }
+
+    @Test
+    public void getNode() throws RepositoryException {
+        Node node = getNode("/foo");
+        assertNotNull(node);
+        assertEquals("foo", node.getName());
+        assertEquals("/foo", node.getPath());
+    }
+
+    @Test
+    @Ignore  // todo implement getNodeByIdentifier
+    public void getNodeByIdentifier() throws RepositoryException {
+        Node node = getNode("/foo");
+        String id = node.getIdentifier();
+        Node node2 = getSession().getNodeByIdentifier(id);
+        assertTrue(node.isSame(node2));
+    }
+
+    @Test
+    public void getNodeFromNode() throws RepositoryException {
+        Node root = getNode("/");
+        Node node = root.getNode("foo");
+        assertNotNull(node);
+        assertEquals("foo", node.getName());
+        assertEquals("/foo", node.getPath());
+
+        Node nodeAgain = getNode("/foo");
+        assertTrue(node.isSame(nodeAgain));
+    }
+
+    @Test
+    public void getNodes() throws RepositoryException {
+        Set<String> nodeNames = new HashSet<String>() {{
+            add("foo");
+            add("bar");
+            add(TEST_NODE);
+        }};
+
+        Node root = getNode("/");
+        NodeIterator nodes = root.getNodes();
+        while (nodes.hasNext()) {
+            assertTrue(nodeNames.remove(nodes.nextNode().getName()));
+        }
+
+        assertTrue(nodeNames.isEmpty());
+    }
+
+    @Test
+    public void getProperties() throws RepositoryException {
+        Set<String> propertyNames = new HashSet<String>() {{
+            add("stringProp");
+            add("intProp");
+            add("mvProp");
+        }};
+        
+        Set<String> values = new HashSet<String>() {{
+            add("stringVal");
+            add("1");
+            add("2");
+            add("3");
+            add("42");
+        }};
+
+        Node node = getNode("/foo");
+        PropertyIterator properties = node.getProperties();
+        while (properties.hasNext()) {
+            Property p = properties.nextProperty();
+            assertTrue(propertyNames.remove(p.getName()));
+            if (p.isMultiple()) {
+                for (Value v : p.getValues()) {
+                    assertTrue(values.remove(v.getString()));
+                }
+            }
+            else {
+                assertTrue(values.remove(p.getString()));
+            }
+        }
+
+        assertTrue(propertyNames.isEmpty());
+        assertTrue(values.isEmpty());
+    }
+
+    @Test(expected = PathNotFoundException.class)
+    public void getNonExistingNode() throws RepositoryException {
+        getNode("/qoo");
+    }
+
+    @Test
+    public void getProperty() throws RepositoryException {
+        Property property = getProperty("/foo/stringProp");
+        assertNotNull(property);
+        assertEquals("stringProp", property.getName());
+        assertEquals("/foo/stringProp", property.getPath());
+
+        Value value = property.getValue();
+        assertNotNull(value);
+        assertEquals(PropertyType.STRING, value.getType());
+        assertEquals("stringVal", value.getString());
+    }
+
+    @Test
+    public void getPropertyFromNode() throws RepositoryException {
+        Node node = getNode("/foo");
+        Property property = node.getProperty("stringProp");
+        assertNotNull(property);
+        assertEquals("stringProp", property.getName());
+        assertEquals("/foo/stringProp", property.getPath());
+
+        Value value = property.getValue();
+        assertNotNull(value);
+        assertEquals(PropertyType.STRING, value.getType());
+        assertEquals("stringVal", value.getString());
+
+        Property propertyAgain = getProperty("/foo/stringProp");
+        assertTrue(property.isSame(propertyAgain));
+    }
+
+    @Test
+    public void addNode() throws RepositoryException {
+        Session session = getSession();
+        String newNode = TEST_PATH + "/new";
+        assertFalse(session.nodeExists(newNode));
+
+        Node node = getNode(TEST_PATH);
+        node.addNode("new");
+        session.save();
+
+        Session session2 = getRepository().login();
+        try {
+            assertTrue(session2.nodeExists(newNode));
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addNodeWithSpecialChars() throws RepositoryException {
+        Session session = getSession();
+        String nodeName = "foo{";
+
+        String newNode = TEST_PATH + '/' + nodeName;
+        assertFalse(session.nodeExists(newNode));
+
+        Node node = getNode(TEST_PATH);
+        node.addNode(nodeName);
+        session.save();
+
+        Session session2 = getRepository().login();
+        try {
+            assertTrue(session2.nodeExists(newNode));
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addNodeWithNodeType() throws RepositoryException {
+        Session session = getSession();
+
+        Node node = getNode(TEST_PATH);
+        node.addNode("new", "nt:folder");
+        session.save();
+    }
+
+    @Test
+    public void addNodeToRootNode() throws RepositoryException {
+        Session session = getSession();
+        Node root = session.getRootNode();
+        String newNode = "newNodeBelowRoot";
+        assertFalse(root.hasNode(newNode));
+        root.addNode(newNode);
+        session.save();
+    }
+
+    @Test
+    public void addStringProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "string", getSession().getValueFactory().createValue("string value"));
+    }
+
+    @Test
+    public void addMultiValuedString() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue("one");
+        values[1] = getSession().getValueFactory().createValue("two");
+
+        parentNode.setProperty("multi string", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi string");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.STRING, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addLongProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "long", getSession().getValueFactory().createValue(42L));
+    }
+
+    @Test
+    public void addMultiValuedLong() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(42L);
+        values[1] = getSession().getValueFactory().createValue(84L);
+
+        parentNode.setProperty("multi long", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi long");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.LONG, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addDoubleProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "double", getSession().getValueFactory().createValue(42.2D));
+    }
+
+    @Test
+    public void addMultiValuedDouble() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(42.1d);
+        values[1] = getSession().getValueFactory().createValue(99.0d);
+
+        parentNode.setProperty("multi double", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi double");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.DOUBLE, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addBooleanProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "boolean", getSession().getValueFactory().createValue(true));
+    }
+
+    @Test
+    public void addMultiValuedBoolean() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(true);
+        values[1] = getSession().getValueFactory().createValue(false);
+
+        parentNode.setProperty("multi boolean", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi boolean");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.BOOLEAN, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addDecimalProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "decimal", getSession().getValueFactory().createValue(BigDecimal.valueOf(21)));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedDecimal() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(BigDecimal.valueOf(42));
+        values[1] = getSession().getValueFactory().createValue(BigDecimal.valueOf(99));
+
+        parentNode.setProperty("multi decimal", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi decimal");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.DECIMAL, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addDateProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "date", getSession().getValueFactory().createValue(Calendar.getInstance()));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedDate() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        Calendar calendar = Calendar.getInstance();
+        values[0] = getSession().getValueFactory().createValue(calendar);
+        calendar.add(Calendar.DAY_OF_MONTH, 1);
+        values[1] = getSession().getValueFactory().createValue(calendar);
+
+        parentNode.setProperty("multi date", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi date");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.DATE, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addURIProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "uri", getSession().getValueFactory().createValue("http://www.day.com/", PropertyType.URI));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedURI() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue("http://www.day.com", PropertyType.URI);
+        values[1] = getSession().getValueFactory().createValue("file://var/dam", PropertyType.URI);
+
+        parentNode.setProperty("multi uri", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi uri");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.URI, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addNameProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "name", getSession().getValueFactory().createValue("jcr:something\"", PropertyType.NAME));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedName() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue("jcr:foo", PropertyType.NAME);
+        values[1] = getSession().getValueFactory().createValue("bar", PropertyType.NAME);
+
+        parentNode.setProperty("multi name", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi name");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.NAME, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addPathProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "path", getSession().getValueFactory().createValue("/jcr:foo/bar\"", PropertyType.PATH));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedPath() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue("/nt:foo/jcr:bar", PropertyType.PATH);
+        values[1] = getSession().getValueFactory().createValue("/", PropertyType.PATH);
+
+        parentNode.setProperty("multi path", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi path");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.PATH, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addBinaryProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        InputStream is = new ByteArrayInputStream("foo\"".getBytes());
+        Binary bin = getSession().getValueFactory().createBinary(is);
+        addProperty(parentNode, "binary", getSession().getValueFactory().createValue(bin));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addSmallBinaryProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        InputStream is = new NumberStream(1234);
+        Binary bin = getSession().getValueFactory().createBinary(is);
+        addProperty(parentNode, "bigBinary", getSession().getValueFactory().createValue(bin));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addBigBinaryProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        InputStream is = new NumberStream(123456);
+        Binary bin = getSession().getValueFactory().createBinary(is);
+        addProperty(parentNode, "bigBinary", getSession().getValueFactory().createValue(bin));
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addMultiValuedBinary() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+
+        InputStream is = new ByteArrayInputStream("foo".getBytes());
+        Binary bin = getSession().getValueFactory().createBinary(is);
+        values[0] = getSession().getValueFactory().createValue(bin);
+
+        is = new ByteArrayInputStream("bar".getBytes());
+        bin = getSession().getValueFactory().createBinary(is);
+        values[1] = getSession().getValueFactory().createValue(bin);
+
+        parentNode.setProperty("multi binary", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi binary");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.BINARY, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void addReferenceProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        Node referee = getSession().getNode("/foo");
+        referee.addMixin("mix:referenceable");
+        getSession().save();
+
+        addProperty(parentNode, "reference", getSession().getValueFactory().createValue(referee));
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void addMultiValuedReference() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Node referee = getSession().getNode("/foo");
+        referee.addMixin("mix:referenceable");
+        getSession().save();
+
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(referee);
+        values[1] = getSession().getValueFactory().createValue(referee);
+
+        parentNode.setProperty("multi reference", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi reference");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.REFERENCE, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void addWeakReferenceProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        Node referee = getSession().getNode("/foo");
+        referee.addMixin("mix:referenceable");
+        getSession().save();
+
+        addProperty(parentNode, "weak reference", getSession().getValueFactory().createValue(referee, true));
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void addMultiValuedWeakReference() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Node referee = getSession().getNode("/foo");
+        referee.addMixin("mix:referenceable");
+        getSession().save();
+
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue(referee, true);
+        values[1] = getSession().getValueFactory().createValue(referee, true);
+
+        parentNode.setProperty("multi weak reference", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi weak reference");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.WEAKREFERENCE, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length, values2.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addEmptyMultiValuedProperty() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[0];
+
+        parentNode.setProperty("multi empty", values, PropertyType.BOOLEAN);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi empty");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.BOOLEAN, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(0, values2.length);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void addEmptyMultiValuedProperty_JCR_2992_WorkaroundTest() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[0];
+
+        parentNode.setProperty("multi empty", values, PropertyType.BOOLEAN);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi empty");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.STRING, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(0, values2.length);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void addMultiValuedStringWithNull() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[3];
+        values[0] = getSession().getValueFactory().createValue(true);
+        values[2] = getSession().getValueFactory().createValue(false);
+
+        parentNode.setProperty("multi with null", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi with null");
+            assertTrue(property.isMultiple());
+            assertEquals(PropertyType.BOOLEAN, property.getType());
+            Value[] values2 = property.getValues();
+            assertEquals(values.length - 1, values2.length);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void setStringProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "string", getSession().getValueFactory().createValue("string value"));
+
+        Property property = parentNode.getProperty("string");
+        property.setValue("new value");
+        property.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property2 = session2.getProperty(TEST_PATH + "/string");
+            assertEquals("new value", property2.getString());
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement value coding in ValueConverter
+    public void setDoubleNaNProperty() throws RepositoryException, IOException {
+        Node parentNode = getNode(TEST_PATH);
+        addProperty(parentNode, "NaN", getSession().getValueFactory().createValue(Double.NaN));
+
+        Session session2 = getRepository().login();
+        try {
+            Property property2 = session2.getProperty(TEST_PATH + "/NaN");
+            assertTrue(Double.isNaN(property2.getDouble()));
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void setMultiValuedProperty() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        Value[] values = new Value[2];
+        values[0] = getSession().getValueFactory().createValue("one");
+        values[1] = getSession().getValueFactory().createValue("two");
+
+        parentNode.setProperty("multi string2", values);
+        parentNode.getSession().save();
+
+        values[0] = getSession().getValueFactory().createValue("eins");
+        values[1] = getSession().getValueFactory().createValue("zwei");
+        parentNode.setProperty("multi string2", values);
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            Property property = session2.getProperty(TEST_PATH + "/multi string2");
+            assertTrue(property.isMultiple());
+            Value[] values2 = property.getValues();
+            assertEquals(2, values.length);
+            assertEquals(values[0], values2[0]);
+            assertEquals(values[1], values2[1]);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void nullProperty() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        parentNode.setProperty("newProperty", "some value");
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            session2.getProperty(TEST_PATH + "/newProperty").setValue((String) null);
+            session2.save();
+        }
+        finally {
+            session2.logout();
+        }
+
+        Session session3 = getRepository().login();
+        try {
+            assertFalse(session3.propertyExists(TEST_PATH + "/newProperty"));
+        }
+        finally {
+            session3.logout();
+        }
+    }
+
+    @Test
+    public void removeProperty() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        parentNode.setProperty("newProperty", "some value");
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            session2.getProperty(TEST_PATH + "/newProperty").remove();
+            session2.save();
+        }
+        finally {
+            session2.logout();
+        }
+
+        Session session3 = getRepository().login();
+        try {
+            assertFalse(session3.propertyExists(TEST_PATH + "/newProperty"));
+        }
+        finally {
+            session3.logout();
+        }
+    }
+
+    @Test
+    public void removeNode() throws RepositoryException {
+        Node parentNode = getNode(TEST_PATH);
+        parentNode.addNode("newNode");
+        parentNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            session2.getNode(TEST_PATH + "/newNode").remove();
+            session2.save();
+        }
+        finally {
+            session2.logout();
+        }
+
+        Session session3 = getRepository().login();
+        try {
+            assertFalse(session3.nodeExists(TEST_PATH + "/newNode"));
+        }
+        finally {
+            session3.logout();
+        }
+    }
+    
+    @Test
+    public void testSessionSave() throws RepositoryException {
+        Session session1 = getRepository().login();
+        Session session2 = getRepository().login();
+        try {
+            // Add some items and ensure they are accessible through this session
+            session1.getNode("/").addNode("node1");
+            session1.getNode("/node1").addNode("node2");
+            session1.getNode("/").addNode("node1/node3");
+
+            Node node1 = session1.getNode("/node1");
+            assertEquals("/node1", node1.getPath());
+            
+            Node node2 = session1.getNode("/node1/node2");
+            assertEquals("/node1/node2", node2.getPath());
+            
+            Node node3 = session1.getNode("/node1/node3");
+            assertEquals("/node1/node3", node3.getPath());
+
+            node3.setProperty("property1", "value1");
+            Item property1 = session1.getProperty("/node1/node3/property1");
+            assertFalse(property1.isNode());
+            assertEquals("value1", ((Property) property1).getString());
+
+            // Make sure these items are not accessible through another session
+            assertFalse(session2.itemExists("/node1"));
+            assertFalse(session2.itemExists("/node1/node2"));
+            assertFalse(session2.itemExists("/node1/node3"));
+            assertFalse(session2.itemExists("/node1/node3/property1"));
+
+            session1.save();
+
+            // Make sure these items are still not accessible through another session until refresh
+            assertFalse(session2.itemExists("/node1"));
+            assertFalse(session2.itemExists("/node1/node2"));
+            assertFalse(session2.itemExists("/node1/node3"));
+            assertFalse(session2.itemExists("/node1/node3/property1"));
+
+            session2.refresh(false);
+            assertTrue(session2.itemExists("/node1"));
+            assertTrue(session2.itemExists("/node1/node2"));
+            assertTrue(session2.itemExists("/node1/node3"));
+            assertTrue(session2.itemExists("/node1/node3/property1"));
+        }
+        finally {
+            session1.logout();
+            session2.logout();
+        }
+    }
+
+    @Test
+    public void testSessionRefresh() throws RepositoryException {
+        Session session = getRepository().login();
+        try {
+            // Add some items and ensure they are accessible through this session
+            session.getNode("/").addNode("node1");
+            session.getNode("/node1").addNode("node2");
+            session.getNode("/").addNode("node1/node3");
+
+            Node node1 = session.getNode("/node1");
+            assertEquals("/node1", node1.getPath());
+
+            Node node2 = session.getNode("/node1/node2");
+            assertEquals("/node1/node2", node2.getPath());
+
+            Node node3 = session.getNode("/node1/node3");
+            assertEquals("/node1/node3", node3.getPath());
+
+            node3.setProperty("property1", "value1");
+            Item property1 = session.getProperty("/node1/node3/property1");
+            assertFalse(property1.isNode());
+            assertEquals("value1", ((Property) property1).getString());
+
+            // Make sure these items are still accessible after refresh(true);
+            session.refresh(true);
+            assertTrue(session.itemExists("/node1"));
+            assertTrue(session.itemExists("/node1/node2"));
+            assertTrue(session.itemExists("/node1/node3"));
+            assertTrue(session.itemExists("/node1/node3/property1"));
+
+            session.refresh(false);
+            // Make sure these items are not accessible after refresh(false);
+            assertFalse(session.itemExists("/node1"));
+            assertFalse(session.itemExists("/node1/node2"));
+            assertFalse(session.itemExists("/node1/node3"));
+            assertFalse(session.itemExists("/node1/node3/property1"));
+        }
+        finally {
+            session.logout();
+        }
+    }
+
+    @Test
+    public void testRefreshConflict() throws RepositoryException {
+        Session session1 = getRepository().login();
+        Session session2 = getRepository().login();
+        try {
+            session1.getRootNode().addNode("node");
+            session2.getRootNode().addNode("node");
+
+            session1.save();
+            session2.refresh(true);
+            try {
+                session2.save();
+                fail();
+            }
+            catch (RepositoryException e) {
+                // expected
+            }
+        }
+        finally {
+            session1.logout();
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void setPrimaryType() throws RepositoryException {
+        Node testNode = getNode(TEST_PATH);
+        assertEquals("nt:unstructured", testNode.getPrimaryNodeType().getName());
+        assertEquals("nt:unstructured", testNode.getProperty("jcr:primaryType").getString());
+
+        testNode.setPrimaryType("nt:folder");
+        getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            testNode = session2.getNode(TEST_PATH);
+            assertEquals("nt:folder", testNode.getPrimaryNodeType().getName());
+            assertEquals("nt:folder", testNode.getProperty("jcr:primaryType").getString());
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement orderBefore
+    public void reorderTest() throws RepositoryException {
+        Node testNode = getNode(TEST_PATH);
+        testNode.addNode("a");
+        testNode.addNode("b");
+        testNode.addNode("c");
+        getSession().save();
+
+        NodeIterator it = testNode.getNodes();
+        while (it.hasNext()) {
+            System.out.println(it.nextNode().getPath());
+        }
+
+        testNode.orderBefore("a", "c");
+        getSession().save();
+
+        it = testNode.getNodes();
+        while (it.hasNext()) {
+            System.out.println(it.nextNode().getPath());
+        }
+        
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void nodeTypeRegistry() throws RepositoryException {
+        NodeTypeManager ntMgr = getSession().getWorkspace().getNodeTypeManager();
+        assertFalse(ntMgr.hasNodeType("foo"));
+
+        NodeTypeTemplate ntd = ntMgr.createNodeTypeTemplate();
+        ntd.setName("foo");
+        ntMgr.registerNodeType(ntd, false);
+        assertTrue(ntMgr.hasNodeType("foo"));
+
+        ntMgr.unregisterNodeType("foo");
+        assertFalse(ntMgr.hasNodeType("foo"));
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void nameSpaceRegistry() throws RepositoryException {
+        NamespaceRegistry nsReg = getSession().getWorkspace().getNamespaceRegistry();
+        assertFalse(contains(nsReg.getPrefixes(), "foo"));
+        assertFalse(contains(nsReg.getURIs(), "file:///foo"));
+
+        nsReg.registerNamespace("foo", "file:///foo");
+        assertTrue(contains(nsReg.getPrefixes(), "foo"));
+        assertTrue(contains(nsReg.getURIs(), "file:///foo"));
+
+        nsReg.unregisterNamespace("foo");
+        assertFalse(contains(nsReg.getPrefixes(), "foo"));
+        assertFalse(contains(nsReg.getURIs(), "file:///foo"));
+    }
+
+    @Test
+    @Ignore // todo implement node type support
+    public void mixin() throws RepositoryException {
+        NodeTypeManager ntMgr = getSession().getWorkspace().getNodeTypeManager();
+        NodeTypeTemplate mixTest = ntMgr.createNodeTypeTemplate();
+        mixTest.setName("mix:test");
+        mixTest.setMixin(true);
+        ntMgr.registerNodeType(mixTest, false);
+
+        Node testNode = getNode(TEST_PATH);
+        NodeType[] mix = testNode.getMixinNodeTypes();
+        assertEquals(0, mix.length);
+
+        testNode.addMixin("mix:test");
+        testNode.getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            mix = session2.getNode(TEST_PATH).getMixinNodeTypes();
+            assertEquals(1, mix.length);
+            assertEquals("mix:test", mix[0].getName());
+        }
+        finally {
+            session2.logout();
+        }
+
+        testNode.removeMixin("mix:test");
+        testNode.getSession().save();
+
+        session2 = getRepository().login();
+        try {
+            mix = session2.getNode(TEST_PATH).getMixinNodeTypes();
+            assertEquals(0, mix.length);
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    @Test
+    @Ignore // todo implement observation
+    public void observation() throws RepositoryException {
+        final Set<String> addNodes = Arrays.toSet(
+                TEST_PATH + "/1",
+                TEST_PATH + "/2",
+                TEST_PATH + "/3");
+
+        final Set<String> removeNodes = Arrays.toSet(
+                TEST_PATH + "/2");
+
+        final Set<String> addProperties = Arrays.toSet(
+                TEST_PATH + "/property",
+                TEST_PATH + "/prop0",
+                TEST_PATH + "/1/prop1",
+                TEST_PATH + "/1/prop2",
+                TEST_PATH + "/1/jcr:primaryType",
+                TEST_PATH + "/2/jcr:primaryType",
+                TEST_PATH + "/3/jcr:primaryType",
+                TEST_PATH + "/3/prop3");
+
+        final Set<String> setProperties = Arrays.toSet(
+                TEST_PATH + "/1/prop1");
+
+        final Set<String> removeProperties = Arrays.toSet(
+                TEST_PATH + "/1/prop2");
+
+        final List<Event> failedEvents = new ArrayList<Event>();
+
+        ObservationManager obsMgr = getSession().getWorkspace().getObservationManager();
+        obsMgr.setUserData("my user data");
+        obsMgr.addEventListener(new EventListener() {
+                public void onEvent(EventIterator events) {
+                    while (events.hasNext()) {
+                        Event event = events.nextEvent();
+                        try {
+                            switch (event.getType()) {
+                                case Event.NODE_ADDED:
+                                    if (!addNodes.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.NODE_REMOVED:
+                                    if (!removeNodes.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.PROPERTY_ADDED:
+                                    if (!addProperties.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.PROPERTY_CHANGED:
+                                    if (!setProperties.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.PROPERTY_REMOVED:
+                                    if (!removeProperties.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                default:
+                                    failedEvents.add(event);
+                            }
+                        }
+                        catch (RepositoryException e) {
+                            failedEvents.add(event);
+                        }
+                    }
+                }
+            },
+            Event.NODE_ADDED | Event.NODE_REMOVED | Event.NODE_MOVED | Event.PROPERTY_ADDED |
+            Event.PROPERTY_REMOVED | Event.PROPERTY_CHANGED | Event.PERSIST, "/", true, null, null, false);
+
+        Node n = getNode(TEST_PATH);
+        n.setProperty("prop0", "val0");
+        Node n1 = n.addNode("1");
+        n1.setProperty("prop1", "val1");
+        n1.setProperty("prop2", "val2");
+        n.addNode("2");
+        getSession().save();
+
+        n.setProperty("property", 42);
+        n.addNode("3").setProperty("prop3", "val3");
+        n1.setProperty("prop1", "val1 new");
+        n1.getProperty("prop2").remove();
+        n.getNode("2").remove();
+        getSession().save();
+
+        new Loop() { @Override public void test() {
+            assertTrue(failedEvents.isEmpty());
+            assertTrue(addNodes.isEmpty());
+            assertTrue(removeNodes.isEmpty());
+            assertTrue(addProperties.isEmpty());
+            assertTrue(removeProperties.isEmpty());
+            assertTrue(setProperties.isEmpty());
+        }};
+    }
+
+    @Test
+    @Ignore // todo implement observation
+    public void observation2() throws RepositoryException {
+        final Set<String> addNodes = Arrays.toSet(
+                TEST_PATH + "/1",
+                TEST_PATH + "/2");
+
+        final Set<String> removeNodes = Arrays.toSet(
+                TEST_PATH + "/1");
+
+        final Set<String> addProperties = Arrays.toSet(
+                TEST_PATH + "/1/jcr:primaryType",
+                TEST_PATH + "/2/jcr:primaryType");
+
+
+        final List<Event> failedEvents = new ArrayList<Event>();
+
+        ObservationManager obsMgr = getSession().getWorkspace().getObservationManager();
+        obsMgr.setUserData("my user data");
+        obsMgr.addEventListener(new EventListener() {
+                public void onEvent(EventIterator events) {
+                    while (events.hasNext()) {
+                        Event event = events.nextEvent();
+                        try {
+                            switch (event.getType()) {
+                                case Event.NODE_ADDED:
+                                    if (!addNodes.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.NODE_REMOVED:
+                                    if (!removeNodes.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                case Event.PROPERTY_ADDED:
+                                    if (!addProperties.remove(event.getPath())) {
+                                        failedEvents.add(event);
+                                    }
+                                    break;
+                                default:
+                                    failedEvents.add(event);
+                            }
+                        }
+                        catch (RepositoryException e) {
+                            failedEvents.add(event);
+                        }
+                    }
+                }
+            },
+            Event.NODE_ADDED | Event.NODE_REMOVED | Event.NODE_MOVED | Event.PROPERTY_ADDED |
+            Event.PROPERTY_REMOVED | Event.PROPERTY_CHANGED | Event.PERSIST, "/", true, null, null, false);
+
+        Node n = getNode(TEST_PATH);
+        n.addNode("1");
+        getSession().save();
+
+        n.addNode("2");
+        n.getNode("1").remove();
+        getSession().save();
+
+        new Loop() { @Override public void test() {
+            assertTrue(failedEvents.isEmpty());
+            assertTrue(addNodes.isEmpty());
+            assertTrue(removeNodes.isEmpty());
+            assertTrue(addProperties.isEmpty());
+        }};
+    }
+
+    @Test
+    @Ignore // todo implement observation
+    public void observationNoEvents() throws RepositoryException, InterruptedException {
+        final List<Event> failedEvents = new ArrayList<Event>();
+
+        ObservationManager obsMgr = getSession().getWorkspace().getObservationManager();
+        obsMgr.setUserData("my user data");
+        obsMgr.addEventListener(new EventListener() {
+                public void onEvent(EventIterator events) {
+                    while (events.hasNext()) {
+                        failedEvents.add(events.nextEvent());
+                    }
+                }
+            },
+            Event.NODE_ADDED | Event.NODE_REMOVED | Event.NODE_MOVED | Event.PROPERTY_ADDED |
+            Event.PROPERTY_REMOVED | Event.PROPERTY_CHANGED | Event.PERSIST, "/", true, null, null, false);
+
+        Thread.sleep(5000);
+        assertTrue(failedEvents.isEmpty());
+    }
+
+    @Test
+    @Ignore // todo implement observation
+    public void observationDispose() throws RepositoryException, ExecutionException, TimeoutException,
+                InterruptedException {
+
+        final List<Event> failedEvents = new ArrayList<Event>();
+
+        final ObservationManager obsMgr = getSession().getWorkspace().getObservationManager();
+        obsMgr.setUserData("my user data");
+        final EventListener listener = new EventListener() {
+            public void onEvent(EventIterator events) {
+                while (events.hasNext()) {
+                    failedEvents.add(events.nextEvent());
+                }
+            }
+        };
+        obsMgr.addEventListener(listener, Event.NODE_ADDED | Event.NODE_REMOVED | Event.NODE_MOVED |
+            Event.PROPERTY_ADDED | Event.PROPERTY_REMOVED | Event.PROPERTY_CHANGED | Event.PERSIST,
+            "/", true, null, null, false);
+
+        FutureTask<Object> disposer = new FutureTask<Object>(new Callable<Object>() {
+            public Object call() throws Exception {
+                obsMgr.removeEventListener(listener);
+                return null;
+            }
+        });
+
+        Thread.sleep(250);
+        Executors.newSingleThreadExecutor().execute(disposer);
+        disposer.get(10000, TimeUnit.MILLISECONDS);
+        assertTrue(failedEvents.isEmpty());
+    }
+
+    /**
+     * Trans-session isolation differs from Jackrabbit 2. Snapshot isolation can
+     * result in write skew as this test demonstrates: the check method enforces
+     * an application logic constraint which says that the sum of the properties
+     * p1 and p2 must not be negative. While session1 and session2 each enforce
+     * this constraint before saving, the constraint might not hold globally as
+     * can be seen in session3.
+     *
+     * @see <a href="http://wiki.apache.org/jackrabbit/Transactional%20model%20of%20the%20Microkernel%20based%20Jackrabbit%20prototype">
+     *     Transactional model of the Microkernel based Jackrabbit prototype</a>
+     */
+    @Test
+    public void testSessionIsolation() throws RepositoryException {
+        Repository repository = getRepository();
+
+        Session session0 = repository.login();
+        Node testNode = session0.getNode("/").addNode("testNode");
+        testNode.setProperty("p1", 1);
+        testNode.setProperty("p2", 1);
+        session0.save();
+        check(getSession());
+
+        Session session1 = repository.login();
+        Session session2 = repository.login();
+
+        session1.getNode("/testNode").setProperty("p1", -1);
+        check(session1);
+        session1.save();
+
+        session2.getNode("/testNode").setProperty("p2", -1);
+        check(session2);      // Throws on JR2, not on JR3
+        session2.save();
+
+        Session session3 = repository.login();
+        try {
+            check(session3);  // Throws on JR3
+            fail();
+        }
+        catch (AssertionError e) {
+            // expected
+        }
+
+        session0.logout();
+        session1.logout();
+        session2.logout();
+        session3.logout();
+    }
+
+    private static void check(Session session) throws RepositoryException {
+        if (session.getNode("/testNode").getProperty("p1").getLong() +
+                session.getNode("/testNode").getProperty("p2").getLong() < 0) {
+            fail("p1 + p2 < 0");
+        }
+    }
+
+    //------------------------------------------< private >---
+
+    private void addProperty(Node parentNode, String name, Value value) throws RepositoryException, IOException {
+        String propertyPath = parentNode.getPath() + '/' + name;
+        assertFalse(getSession().propertyExists(propertyPath));
+
+        parentNode.setProperty(name, value);
+        getSession().save();
+
+        Session session2 = getRepository().login();
+        try {
+            assertTrue(session2.propertyExists(propertyPath));
+            Value value2 = session2.getProperty(propertyPath).getValue();
+            assertEquals(value.getType(), value2.getType());
+            assertEqualStream(value.getStream(), value2.getStream());
+            assertEquals(value.getString(), value2.getString());
+
+            if (value2.getType() == PropertyType.REFERENCE || value2.getType() == PropertyType.WEAKREFERENCE) {
+                String ref = value2.getString();
+                assertNotNull(getSession().getNodeByIdentifier(ref));
+            }
+        }
+        finally {
+            session2.logout();
+        }
+    }
+
+    private static void assertEqualStream(InputStream is1, InputStream is2) throws IOException {
+        byte[] buf1 = new byte[65536];
+        byte[] buf2 = new byte[65536];
+
+        int c = 0;
+        while (c != -1) {
+            assertEquals(c = is1.read(buf1), is2.read(buf2));
+            for (int i = 0; i < c; i++) {
+                assertEquals(buf1[i], buf2[i]);
+            }
+        }
+    }
+
+    private Repository getRepository() throws RepositoryException {
+        if (repository == null) {
+            Iterator<RepositoryFactory> factories = ServiceRegistry.lookupProviders(RepositoryFactory.class);
+            while (repository == null && factories.hasNext()) {
+                RepositoryFactory factory = factories.next();
+                repository = factory.getRepository(Collections.singletonMap(
+                        RepositoryConfiguration.MICROKERNEL_URL, URL));
+            }
+        }
+
+        return repository;
+    }
+
+    private Session getSession() throws RepositoryException {
+        if (session == null) {
+            session = getRepository().login(new GuestCredentials());
+        }
+        return session;
+    }
+
+    private Node getNode(String path) throws RepositoryException {
+        return getSession().getNode(path);
+    }
+
+    private Property getProperty(String path) throws RepositoryException {
+        return getSession().getProperty(path);
+    }
+}

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/RepositoryTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java Thu Dec  8 18:18:42 2011
@@ -0,0 +1,120 @@
+/*
+ * 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.json;
+
+import org.apache.jackrabbit.json.Token.Type;
+import org.junit.Test;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+public class DefaultJsonTokenizerTest {
+
+    private static final Token[] TOKENS = new Token[] {
+        new Token(Type.BEGIN_OBJECT, "{", 0),
+        new Token(Type.END_OBJECT, "}", 2),
+        new Token(Type.BEGIN_ARRAY, "[", 4),
+        new Token(Type.END_ARRAY, "]", 6),
+        new Token(Type.COLON, ":", 8),
+        new Token(Type.COMMA, ",", 10),
+        new Token(Type.TRUE, "true", 12),
+        new Token(Type.FALSE, "false", 17),
+        new Token(Type.NULL, "null", 23),
+        new Token(Type.STRING, "string", 28),
+        new Token(Type.NUMBER, "-12.34e-56", 37),
+        new Token(Type.STRING, "ab\\\"", 48),
+        new Token(Type.STRING, "ab\\\\", 55),
+        new Token(Type.STRING, "", 62),
+        new Token(Type.STRING, "\\\"", 65),
+        new Token(Type.STRING, "\\\\", 70),
+        new Token(Type.UNKNOWN, "qwe", 75)
+};
+
+    private static final String TOKEN_STRING = join(TOKENS);
+
+    private static final Token EOF_TOKEN = new Token(Type.EOF, "", TOKEN_STRING.length());
+
+    @Test
+    public void testRead() {
+        JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+        for (Token token : TOKENS) {
+            int pos = tokenizer.pos();
+            assertEquals(token, tokenizer.read());
+            assertEquals(pos, token.pos());
+        }
+        assertEquals(EOF_TOKEN, tokenizer.read());
+    }
+
+    @Test
+    public void testPeek() {
+        JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+        for (Token token : TOKENS) {
+            Token peeked = tokenizer.peek();
+            assertEquals(token, peeked);
+            assertEquals(token.pos(), peeked.pos());
+
+            for (Token t : TOKENS) {
+                if (t.type() != token.type()) {
+                    assertFalse(tokenizer.peek(t.type()));
+                }
+            }
+            assertEquals(peeked, tokenizer.read(peeked.type()));
+        }
+        assertEquals(EOF_TOKEN, tokenizer.peek());
+    }
+
+    @Test
+    public void testSkip() {
+        JsonTokenizer tokenizer = new DefaultJsonTokenizer(TOKEN_STRING);
+
+        for (Token token : TOKENS) {
+            Token peeked = tokenizer.peek();
+            assertEquals(token, peeked);
+
+            for (Token t : TOKENS) {
+                if (t.type() != token.type()) {
+                    assertFalse(tokenizer.skip(t.type()));
+                }
+            }
+            assertTrue(tokenizer.skip(token.type()));
+        }
+        assertEquals(EOF_TOKEN, tokenizer.peek());
+    }
+
+    //------------------------------------------< private >---
+
+    private static String join(Token[] tokens) {
+        StringBuilder json = new StringBuilder();
+        for (Token token : tokens) {
+            json.append(getString(token)).append(' ');
+        }
+        return json.toString();
+    }
+
+    private static String getString(Token token) {
+        return token.type() == Type.STRING
+            ? '\"' + token.text() + '\"'
+            : token.text();
+    }
+
+}

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/DefaultJsonTokenizerTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/JsonParserTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/JsonParserTest.java?rev=1212018&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/JsonParserTest.java (added)
+++ jackrabbit/sandbox/jackrabbit-microkernel/src/test/java/org/apache/jackrabbit/json/JsonParserTest.java Thu Dec  8 18:18:42 2011
@@ -0,0 +1,208 @@
+/*
+ * 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.json;
+
+import org.apache.jackrabbit.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.json.JsonValue.JsonObject;
+import org.apache.jackrabbit.json.JsonValue.Visitor;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Stack;
+
+import static junit.framework.Assert.assertEquals;
+
+public class JsonParserTest {
+    private static final String JSON_IN = readFile("/test1.json");
+
+    @Test
+    public void testParser() {
+        final StringBuilder jsonOut = new StringBuilder("{");
+        new JsonParser(new JsonHandler() {
+            @Override
+            public void atom(Token key, Token value) {
+                jsonOut.append(createKey(key));
+                if (value.type() == Token.Type.STRING) {
+                    jsonOut.append(quoteAndEscape(value.text()));
+                }
+                else {
+                    jsonOut.append(value.text());
+                }
+            }
+
+            @Override
+            public void comma(Token token) {
+                jsonOut.append(',');
+            }
+
+            @Override
+            public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+                jsonOut.append(createKey(key)).append('{');
+                super.object(parser, key, tokenizer);
+                jsonOut.append('}');
+            }
+
+            @Override
+            public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+                jsonOut.append(createKey(key)).append('[');
+                super.array(parser, key, tokenizer);
+                jsonOut.append(']');
+            }
+
+        }).parseObject(new UnescapingJsonTokenizer(JSON_IN));
+        jsonOut.append('}');
+
+        assertEquals(JSON_IN, jsonOut.toString());
+    }
+
+    @Test
+    public void testParserWithBuilder() {
+        final Stack<JsonValue> objects = new Stack<JsonValue>();
+        objects.push(new JsonObject(new LinkedHashMap<String, JsonValue>()));
+
+        new JsonParser(new JsonHandler() {
+            @Override
+            public void atom(Token key, Token value) {
+               put(key, new JsonAtom(value));
+            }
+
+            @Override
+            public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+                JsonObject object = new JsonObject(new LinkedHashMap<String, JsonValue>());
+                put(key, object);
+                objects.push(object);
+                super.object(parser, key, tokenizer);
+                objects.pop();
+            }
+
+            @Override
+            public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+                JsonArray array = new JsonArray(new ArrayList<JsonValue>());
+                put(key, array);
+                objects.push(array);
+                super.array(parser, key, tokenizer);
+                objects.pop();
+            }
+
+            private void put(final Token key, final JsonValue value) {
+                objects.peek().accept(new Visitor(){
+                    @Override
+                    public void visit(JsonArray array) {
+                        array.add(value);
+                    }
+                    @Override
+                    public void visit(JsonObject object) {
+                        object.put(key.text(), value);
+                    }
+                });
+            }
+
+        }).parseObject(new UnescapingJsonTokenizer(JSON_IN));
+
+        assertEquals(1, objects.size());
+        assertEquals(JSON_IN, objects.peek().toJson());
+    }
+
+
+    @Test(expected = ParseException.class)
+    public void testParseExceptionValue1() {
+        JsonParser parser = new JsonParser(new JsonHandler());
+        parser.parseObject(new UnescapingJsonTokenizer("{\"key\":}"));
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseExceptionValue2() {
+        JsonParser parser = new JsonParser(new JsonHandler());
+        parser.parseObject(new UnescapingJsonTokenizer("{\"key\":[1,]}"));
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseExceptionPair1() {
+        JsonParser parser = new JsonParser(new JsonHandler());
+        parser.parseObject(new UnescapingJsonTokenizer("{\"key\":1,}"));
+    }
+
+    @Test(expected = ParseException.class)
+    public void testParseExceptionPair2() {
+        JsonParser parser = new JsonParser(new JsonHandler());
+        parser.parsePair(new UnescapingJsonTokenizer(""));
+    }
+
+    @Test
+    public void testJson1() {
+        JsonObject object1 = FullJsonParser.parseObject(new UnescapingJsonTokenizer(JSON_IN));
+        assertEquals(JSON_IN, object1.toJson());
+
+        JsonObject object2 = LevelOrderJsonParser.parseObject(new UnescapingJsonTokenizer(JSON_IN));
+        assertEquals(JSON_IN, object2.toJson());
+
+        assertEquals(object1, object2);
+        assertEquals(object1.toJson(), object2.toJson());
+    }
+
+    @Test
+    public void testJson2() {
+        String json = readFile("/test2.json");
+        JsonObject object1 = FullJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+        JsonObject object2 = LevelOrderJsonParser.parseObject(new UnescapingJsonTokenizer(json));
+        assertEquals(object1, object2);
+        assertEquals(object1.toJson(), object2.toJson());
+    }
+
+    //------------------------------------------< private >---
+
+    private static String readFile(String fileName) {
+        InputStream is = JsonParserTest.class.getResourceAsStream(fileName);
+        if (is == null) {
+            throw new RuntimeException("Resource not found: " + fileName);
+        }
+
+        try {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+            String s;
+            StringBuilder sb = new StringBuilder();
+            while((s = reader.readLine()) != null) {
+                sb.append(s);
+            }
+
+            reader.close();
+            return sb.toString();
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String createKey(Token key) {
+        return key == null ? "" : quoteAndEscape(key.text()) + ':';
+    }
+
+    private static String quoteAndEscape(String text) {
+        return '\"' + JsonValue.escape(text) + '\"';
+    }
+
+}