You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-dev@jackrabbit.apache.org by md...@apache.org on 2012/03/15 14:33:33 UTC

svn commit: r1300973 [1/3] - in /jackrabbit/oak/trunk/oak-jcr: ./ src/main/java/org/apache/jackrabbit/oak/jcr/ src/main/java/org/apache/jackrabbit/oak/jcr/configuration/ src/main/java/org/apache/jackrabbit/oak/jcr/json/ src/main/java/org/apache/jackrab...

Author: mduerig
Date: Thu Mar 15 13:33:32 2012
New Revision: 1300973

URL: http://svn.apache.org/viewvc?rev=1300973&view=rev
Log:
OAK-5: JCR bindings for Oak
pass CRUDTest

Added:
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/GlobalContext.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/GlobalContext.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionContext.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionContext.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionFactory.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionFactory.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/OakRepositoryConfiguration.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/RepositoryConfiguration.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/DefaultJsonTokenizer.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/FullJsonParser.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonHandler.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonParser.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonTokenizer.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonValue.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsopHandler.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsopParser.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/LevelOrderJsonParser.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/ParseException.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/Token.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/UnescapingJsonTokenizer.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/Authenticator.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/AuthenticatorImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/CredentialsInfo.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/ChangeLog.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/ChangeTree.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/EmptyNodeState.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/NodeStateProvider.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/PersistentNodeState.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/PropertyStateImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/TransientNodeState.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/state/TransientSpace.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Function0.java
      - copied, changed from r1299739, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Function0.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Function1.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Function1.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ItemNameMatcher.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ItemNameMatcher.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Iterators.java
      - copied, changed from r1299781, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Iterators.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/PagedIterator.java
      - copied, changed from r1299746, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/PagedIterator.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Path.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/Path.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Predicate.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Predicate.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/Unchecked.java
      - copied, changed from r1299739, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/Unchecked.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java
      - copied, changed from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/utils/ValueConverter.java
Modified:
    jackrabbit/oak/trunk/oak-jcr/pom.xml
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/OakRepositoryFactory.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
    jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java

Modified: jackrabbit/oak/trunk/oak-jcr/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/pom.xml?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-jcr/pom.xml Thu Mar 15 13:33:32 2012
@@ -42,6 +42,11 @@
 
     <dependency>
       <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>oak-core</artifactId>
+      <version>0.1-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
       <artifactId>jackrabbit-api</artifactId>
       <version>2.4.0</version>
     </dependency>
@@ -52,6 +57,11 @@
     </dependency>
 
     <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+      <version>3.2.1</version>
+    </dependency>
+    <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>1.6.4</version>

Copied: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/GlobalContext.java (from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/GlobalContext.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/GlobalContext.java?p2=jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/GlobalContext.java&p1=jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/GlobalContext.java&r1=1299713&r2=1300973&rev=1300973&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/GlobalContext.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/GlobalContext.java Thu Mar 15 13:33:32 2012
@@ -17,15 +17,15 @@
  * under the License.
  */
 
-package org.apache.jackrabbit;
+package org.apache.jackrabbit.oak.jcr;
 
 import org.apache.jackrabbit.commons.SimpleValueFactory;
-import org.apache.jackrabbit.configuration.RepositoryConfiguration;
 import org.apache.jackrabbit.mk.MicroKernelFactory;
 import org.apache.jackrabbit.mk.api.MicroKernel;
-import org.apache.jackrabbit.security.Authenticator;
-import org.apache.jackrabbit.security.AuthenticatorImpl;
-import org.apache.jackrabbit.utils.Unchecked;
+import org.apache.jackrabbit.oak.jcr.configuration.RepositoryConfiguration;
+import org.apache.jackrabbit.oak.jcr.security.Authenticator;
+import org.apache.jackrabbit.oak.jcr.security.AuthenticatorImpl;
+import org.apache.jackrabbit.oak.jcr.util.Unchecked;
 
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
@@ -36,16 +36,17 @@ import java.util.Map;
 import static java.text.MessageFormat.format;
 
 /**
- * Poor Java's dependency injection
+ * Poor man's dependency injection
+ * todo: replace by some more sophisticated mechanism
  */
 public class GlobalContext {
     private final Map<Class<?>, Object> instances = new HashMap<Class<?>, Object>();
     
     public GlobalContext(RepositoryConfiguration repositoryConfiguration) throws RepositoryException {
-        put(ValueFactory.class, new SimpleValueFactory());  
+        put(ValueFactory.class, new SimpleValueFactory());
         put(RepositoryConfiguration.class, repositoryConfiguration);
         put(MicroKernel.class, MicroKernelFactory.getInstance(repositoryConfiguration.getMicrokernelUrl()));
-        put(Repository.class, RepositoryImpl.create(this));
+        put(Repository.class, new RepositoryImpl(this));
         put(Authenticator.class, AuthenticatorImpl.INSTANCE);
         put(SessionFactory.class, SessionImpl.FACTORY);
     }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/ItemImpl.java Thu Mar 15 13:33:32 2012
@@ -16,6 +16,9 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import org.apache.jackrabbit.oak.jcr.SessionImpl.Context;
+import org.apache.jackrabbit.oak.jcr.state.TransientNodeState;
+import org.apache.jackrabbit.oak.jcr.util.Path;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,13 +42,16 @@ import javax.jcr.version.VersionExceptio
  * <code>ItemImpl</code>...
  */
 abstract class ItemImpl implements Item {
+    protected final Context sessionContext;
 
     /**
      * logger instance
      */
     private static final Logger log = LoggerFactory.getLogger(ItemImpl.class);
 
-    private final SessionImpl session = null;
+    protected ItemImpl(Context sessionContext) {
+        this.sessionContext = sessionContext;
+    }
 
     @Override
     public String getPath() throws RepositoryException {
@@ -79,8 +85,7 @@ abstract class ItemImpl implements Item 
 
     @Override
     public Session getSession() throws RepositoryException {
-        // TODO
-        return null;
+        return sessionContext.getSession();
     }
 
     @Override
@@ -150,6 +155,11 @@ abstract class ItemImpl implements Item 
     }
 
     //--------------------------------------------------------------------------
+    
+    SessionImpl getOakSession() {
+        return sessionContext.getSession();
+    }
+
     /**
      * Performs a sanity check on this item and the associated session.
      *
@@ -157,7 +167,7 @@ abstract class ItemImpl implements Item 
      */
     void checkStatus() throws RepositoryException {
         // check session status
-        session.checkIsAlive();
+        getOakSession().checkIsAlive();
 
         // TODO: validate item state.
     }
@@ -169,7 +179,7 @@ abstract class ItemImpl implements Item 
      * @throws RepositoryException
      */
     void checkSessionHasPendingChanges() throws RepositoryException {
-        session.checkHasPendingChanges();
+        getOakSession().checkHasPendingChanges();
     }
 
     /**
@@ -179,6 +189,11 @@ abstract class ItemImpl implements Item 
      * @throws RepositoryException
      */
     ValueFactory getValueFactory() throws RepositoryException {
-        return session.getValueFactory();
+        return getOakSession().getValueFactory();
+    }
+
+    protected static TransientNodeState getNodeState(Context sessionContext, Path path) {
+        return sessionContext.getNodeStateProvider().getNodeState(path);
     }
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java Thu Mar 15 13:33:32 2012
@@ -16,7 +16,21 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
+import org.apache.jackrabbit.commons.iterator.PropertyIteratorAdapter;
+import org.apache.jackrabbit.oak.jcr.SessionImpl.Context;
+import org.apache.jackrabbit.oak.jcr.json.JsonValue;
+import org.apache.jackrabbit.oak.jcr.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.oak.jcr.state.PropertyStateImpl;
+import org.apache.jackrabbit.oak.jcr.state.TransientNodeState;
+import org.apache.jackrabbit.oak.jcr.util.Function1;
+import org.apache.jackrabbit.oak.jcr.util.ItemNameMatcher;
+import org.apache.jackrabbit.oak.jcr.util.Iterators;
 import org.apache.jackrabbit.oak.jcr.util.LogUtil;
+import org.apache.jackrabbit.oak.jcr.util.Path;
+import org.apache.jackrabbit.oak.jcr.util.Predicate;
+import org.apache.jackrabbit.oak.jcr.util.ValueConverter;
+import org.apache.jackrabbit.oak.model.PropertyState;
 import org.apache.jackrabbit.value.ValueHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,6 +69,9 @@ import javax.jcr.version.VersionManager;
 import java.io.InputStream;
 import java.math.BigDecimal;
 import java.util.Calendar;
+import java.util.Iterator;
+
+import static org.apache.jackrabbit.oak.jcr.util.Iterators.filter;
 
 /**
  * <code>NodeImpl</code>...
@@ -66,7 +83,76 @@ public class NodeImpl extends ItemImpl i
      */
     private static final Logger log = LoggerFactory.getLogger(NodeImpl.class);
 
+    private final TransientNodeState state;
+
+    static boolean exist(Context sessionContext, Path path) {
+        return getNodeState(sessionContext, path) != null;
+    }
+
+    static Node create(Context sessionContext, Path path) throws PathNotFoundException {
+        TransientNodeState state = getNodeState(sessionContext, path);
+        if (state == null) {
+            throw new PathNotFoundException(path.toJcrPath());
+        }
+
+        return new NodeImpl(sessionContext, state);
+    }
+
+    static Node create(Context sessionContext, TransientNodeState state) {
+        return new NodeImpl(sessionContext, state);
+    }
+
+    private NodeImpl(Context sessionContext, TransientNodeState state) {
+        super(sessionContext);
+        this.state = state;
+    }
+
     //---------------------------------------------------------------< Item >---
+
+    @Override
+    public String getPath() throws RepositoryException {
+        return path().toJcrPath();
+    }
+
+    @Override
+    public String getName() throws RepositoryException {
+        return state.getName();
+    }
+
+    @Override
+    public Item getAncestor(int depth) throws RepositoryException {
+        Path parent = path().getAncestor(depth);
+        if (parent == null) {
+            throw new ItemNotFoundException(path().toJcrPath() + "has no ancestor of depth " + depth);
+        }
+
+        return create(sessionContext, parent);
+    }
+
+    @Override
+    public Node getParent() throws RepositoryException {
+        if (state.isRoot()) {
+            throw new ItemNotFoundException("Root has no parent");
+        }
+
+        return create(sessionContext, path().getParent());
+    }
+
+    @Override
+    public int getDepth() throws RepositoryException {
+        return path().getDepth();
+    }
+
+    @Override
+    public boolean isNew() {
+        return state.isNew();
+    }
+
+    @Override
+    public boolean isModified() {
+        return state.isModified();
+    }
+
     /**
      * @see javax.jcr.Item#isNode()
      */
@@ -90,15 +176,24 @@ public class NodeImpl extends ItemImpl i
      */
     @Override
     public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
-        return addNode(relPath, null);
+        checkStatus();
+        Path newPath = path().concat(relPath);
+        TransientNodeState parentState = getNodeState(sessionContext, newPath.getParent());
+        TransientNodeState childState = parentState.addNode(newPath.getName());
+        return create(sessionContext, childState);
     }
 
     @Override
     public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException {
         checkStatus();
+        Node childNode = addNode(relPath);
+        childNode.setPrimaryType(primaryNodeTypeName);
+        return childNode;
+    }
 
-        // TODO
-        return null;
+    @Override
+    public void remove() throws RepositoryException {
+        state.remove();
     }
 
     @Override
@@ -127,8 +222,8 @@ public class NodeImpl extends ItemImpl i
     public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        state.setProperty(name, ValueConverter.toJsonValue(value));
+        return getProperty(name);
     }
 
     /**
@@ -149,8 +244,8 @@ public class NodeImpl extends ItemImpl i
     public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        state.setProperty(name, ValueConverter.toJsonValue(values));
+        return getProperty(name);
     }
 
     /**
@@ -265,62 +360,88 @@ public class NodeImpl extends ItemImpl i
 
     @Override
     public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
-        // TODO
-        return null;
+        return create(sessionContext, path().concat(relPath));
     }
 
     @Override
     public NodeIterator getNodes() throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        Iterator<TransientNodeState> childNodeStates = state.getNodes();
+        return new NodeIteratorAdapter(nodeIterator(childNodeStates));
     }
 
     @Override
-    public NodeIterator getNodes(String namePattern) throws RepositoryException {
+    public NodeIterator getNodes(final String namePattern) throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        Iterator<TransientNodeState> childNodeStates = filter(state.getNodes(),
+                new Predicate<TransientNodeState>() {
+                    @Override
+                    public boolean evaluate(TransientNodeState state) {
+                        return ItemNameMatcher.matches(state.getName(), namePattern);
+                    }
+                });
+
+        return new NodeIteratorAdapter(nodeIterator(childNodeStates));
     }
 
     @Override
-    public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
+    public NodeIterator getNodes(final String[] nameGlobs) throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        Iterator<TransientNodeState> childNodeStates = filter(state.getNodes(),
+                new Predicate<TransientNodeState>() {
+                    @Override
+                    public boolean evaluate(TransientNodeState state) {
+                        return ItemNameMatcher.matches(state.getName(), nameGlobs);
+                    }
+                });
+
+        return new NodeIteratorAdapter(nodeIterator(childNodeStates));
     }
 
     @Override
     public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        return PropertyImpl.create(sessionContext, path().concat(relPath));
     }
 
     @Override
     public PropertyIterator getProperties() throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        Iterator<PropertyState> properties = state.getProperties();
+        return new PropertyIteratorAdapter(propertyIterator(properties));
     }
 
     @Override
-    public PropertyIterator getProperties(String namePattern) throws RepositoryException {
+    public PropertyIterator getProperties(final String namePattern) throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return null;
+        Iterator<PropertyState> properties = filter(state.getProperties(),
+                new Predicate<PropertyState>() {
+                    @Override
+                    public boolean evaluate(PropertyState entry) {
+                        return ItemNameMatcher.matches(entry.getName(), namePattern);
+                    }
+                });
+
+        return new PropertyIteratorAdapter(propertyIterator(properties));
     }
 
     @Override
-    public PropertyIterator getProperties(String[] nameGlobs) throws RepositoryException {
-        // TODO
-        return null;
+    public PropertyIterator getProperties(final String[] nameGlobs) throws RepositoryException {
+        Iterator<PropertyState> propertyNames = filter(state.getProperties(),
+                new Predicate<PropertyState>() {
+                    @Override
+                    public boolean evaluate(PropertyState entry) {
+                        return ItemNameMatcher.matches(entry.getName(), nameGlobs);
+                    }
+                });
+
+        return new PropertyIteratorAdapter(propertyIterator(propertyNames));
     }
 
     /**
@@ -400,32 +521,28 @@ public class NodeImpl extends ItemImpl i
     public boolean hasNode(String relPath) throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return false;
+        return exist(sessionContext, path().concat(relPath));
     }
 
     @Override
     public boolean hasProperty(String relPath) throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return false;
+        return PropertyImpl.exist(sessionContext, path().concat(relPath));
     }
 
     @Override
     public boolean hasNodes() throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return false;
+        return state.hasNodes();
     }
 
     @Override
     public boolean hasProperties() throws RepositoryException {
         checkStatus();
 
-        // TODO
-        return false;
+        return state.hasProperties();
     }
 
     @Override
@@ -456,22 +573,25 @@ public class NodeImpl extends ItemImpl i
     public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
         checkStatus();
 
-        // TODO
-
+        state.setProperty("jcr:primaryType", JsonAtom.string(nodeTypeName));
     }
 
     @Override
     public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
         checkStatus();
 
-        // TODO
+        JsonValue mixins = state.getPropertyValue("jcr:mixinTypes");
+        mixins.asArray().add(JsonAtom.string(mixinName));
+        state.setProperty("jcr:mixinTypes", mixins);
     }
 
     @Override
     public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
         checkStatus();
 
-        // TODO
+        JsonValue mixins = state.getPropertyValue("jcr:mixinTypes");
+        mixins.asArray().remove(JsonAtom.string(mixinName));
+        state.setProperty("jcr:mixinTypes", mixins);
     }
 
     @Override
@@ -696,4 +816,27 @@ public class NodeImpl extends ItemImpl i
     private VersionManager getVersionManager() throws RepositoryException {
         return getSession().getWorkspace().getVersionManager();
     }
+
+    private Path path() {
+        return state.getPath();
+    }
+
+    private Iterator<Node> nodeIterator(Iterator<TransientNodeState> childNodeStates) {
+        return Iterators.map(childNodeStates, new Function1<TransientNodeState, Node>() {
+            @Override
+            public Node apply(TransientNodeState state) {
+                return NodeImpl.create(sessionContext, state);
+            }
+        });
+    }
+
+    private Iterator<Property> propertyIterator(Iterator<PropertyState> properties) {
+        return Iterators.map(properties, new Function1<PropertyState, Property>() {
+            @Override
+            public Property apply(PropertyState state) { // fixme don't cast
+                JsonValue value = ((PropertyStateImpl) state).getValue();
+                return PropertyImpl.create(sessionContext, NodeImpl.this.state, state.getName(), value);
+            }
+        });
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/OakRepositoryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/OakRepositoryFactory.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/OakRepositoryFactory.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/OakRepositoryFactory.java Thu Mar 15 13:33:32 2012
@@ -16,14 +16,15 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Map;
+import org.apache.jackrabbit.oak.jcr.configuration.OakRepositoryConfiguration;
+import org.apache.jackrabbit.oak.jcr.configuration.RepositoryConfiguration;
 
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.RepositoryFactory;
-import javax.jcr.UnsupportedRepositoryOperationException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
 
 @SuppressWarnings("rawtypes")
 public class OakRepositoryFactory implements RepositoryFactory {
@@ -47,10 +48,12 @@ public class OakRepositoryFactory implem
         return null;
     }
 
-    private Repository getRepository(URI uri, Map parameters)
+    private static Repository getRepository(URI uri, Map parameters)
             throws RepositoryException {
-        throw new UnsupportedRepositoryOperationException(
-                "jcr-oak repository URIs are not yet supported: " + uri);
+
+        parameters.put(RepositoryConfiguration.MICROKERNEL_URL, "simple:target/repository-test/repository");
+        GlobalContext context = new GlobalContext(OakRepositoryConfiguration.create(parameters));
+        return context.getInstance(Repository.class);
     }
 
 }

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/PropertyImpl.java Thu Mar 15 13:33:32 2012
@@ -16,12 +16,18 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import org.apache.jackrabbit.oak.jcr.SessionImpl.Context;
+import org.apache.jackrabbit.oak.jcr.json.JsonValue;
+import org.apache.jackrabbit.oak.jcr.state.TransientNodeState;
 import org.apache.jackrabbit.oak.jcr.util.LogUtil;
+import org.apache.jackrabbit.oak.jcr.util.Path;
+import org.apache.jackrabbit.oak.jcr.util.ValueConverter;
 import org.apache.jackrabbit.value.ValueHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.jcr.Binary;
+import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
@@ -30,6 +36,7 @@ import javax.jcr.Property;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
+import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
 import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
@@ -49,6 +56,38 @@ public class PropertyImpl extends ItemIm
      */
     private static final Logger log = LoggerFactory.getLogger(PropertyImpl.class);
 
+    private final TransientNodeState parentState;
+    private final String name;
+    private final JsonValue value;
+
+    static Property create(Context sessionContext, Path path) throws PathNotFoundException,
+            ItemNotFoundException {
+
+        TransientNodeState parentState = getNodeState(sessionContext, path.getParent());
+        if (parentState == null) {
+            throw new PathNotFoundException(path.toJcrPath());
+        }
+
+        String name = path.getName();
+        JsonValue value = parentState.getPropertyValue(name);
+        return new PropertyImpl(sessionContext, parentState, name, value);
+    }
+
+    static Property create(Context sessionContext, TransientNodeState parentState, String name, JsonValue value) {
+        return new PropertyImpl(sessionContext, parentState, name, value);
+    }
+
+    public static boolean exist(Context sessionContext, Path path) {
+        TransientNodeState parentState = getNodeState(sessionContext, path.getParent());
+        return parentState != null && parentState.hasProperty(path.getName());
+    }
+
+    private PropertyImpl(Context sessionContext, TransientNodeState parentState, String name, JsonValue value) {
+        super(sessionContext);
+        this.parentState = parentState;
+        this.name = name;
+        this.value = value;
+    }
 
     //---------------------------------------------------------------< Item >---
     /**
@@ -59,6 +98,16 @@ public class PropertyImpl extends ItemIm
         return false;
     }
 
+    @Override
+    public String getPath() throws RepositoryException {
+        return parentState.getPath().concat(name).toJcrPath();
+    }
+
+    @Override
+    public String getName() throws RepositoryException {
+        return name;
+    }
+
     /**
      * @see javax.jcr.Item#accept(javax.jcr.ItemVisitor)
      */
@@ -68,6 +117,42 @@ public class PropertyImpl extends ItemIm
         visitor.visit(this);
     }
 
+    @Override
+    public Item getAncestor(int depth) throws RepositoryException {
+        if (depth == getDepth() - 1) {
+            return getParent();
+        }
+        else {
+            return getParent().getAncestor(depth);
+        }
+    }
+
+    @Override
+    public Node getParent() throws RepositoryException {
+        return NodeImpl.create(sessionContext, parentState);
+    }
+
+    @Override
+    public int getDepth() throws RepositoryException {
+        return parentState.getPath().getDepth() + 1;
+    }
+
+    @Override
+    public boolean isNew() {
+        return parentState.isPropertyNew(name);
+    }
+
+    @Override
+    public boolean isModified() {
+        return parentState.isPropertyModified(name);
+    }
+
+    @Override
+    public void remove() throws RepositoryException {
+        parentState.removeProperty(name);
+    }
+
+
     //-----------------------------------------------------------< Property >---
     /**
      * @see Property#setValue(Value)
@@ -256,8 +341,12 @@ public class PropertyImpl extends ItemIm
             throw new ValueFormatException(LogUtil.safeGetJCRPath(this) + " is multi-valued.");
         }
 
-        // TODO
-        return null;
+        if (!value.isAtom()) {
+            throw new ValueFormatException(value.toJson());
+        }
+
+        ValueFactory valueFactory = sessionContext.getValueFactory();
+        return ValueConverter.toValue(valueFactory, value.asAtom());
     }
 
     @Override
@@ -267,8 +356,12 @@ public class PropertyImpl extends ItemIm
             throw new ValueFormatException(LogUtil.safeGetJCRPath(this) + " is not multi-valued.");
         }
 
-        // TODO
-        return new Value[0];
+        if (!value.isArray()) {
+            throw new ValueFormatException(value.toJson());
+        }
+
+        ValueFactory valueFactory = sessionContext.getValueFactory();
+        return ValueConverter.toValue(valueFactory, value.asArray());
     }
 
     /**
@@ -422,14 +515,23 @@ public class PropertyImpl extends ItemIm
 
     @Override
     public int getType() throws RepositoryException {
-        // TODO
-        return 0;
+        if (isMultiple()) {
+            Value[] values = getValues();
+            if (values.length == 0) {
+                return PropertyType.UNDEFINED;
+            }
+            else {
+                return values[0].getType();
+            }
+        }
+        else {
+            return getValue().getType();
+        }
     }
 
     @Override
     public boolean isMultiple() throws RepositoryException {
-        // TODO
-        return false;
+        return value.isArray();
     }
 
     //------------------------------------------------------------< private >---
@@ -464,7 +566,12 @@ public class PropertyImpl extends ItemIm
             throw new IllegalArgumentException("Property type of a value cannot be undefined (" + LogUtil.safeGetJCRPath(this) + ").");
         }
 
-        // TODO -> set value on the property state and remember operation.
+        if (value == null) {
+            remove();
+        }
+        else {
+            parentState.setProperty(name, ValueConverter.toJsonValue(value));
+        }
     }
 
     /**
@@ -479,7 +586,12 @@ public class PropertyImpl extends ItemIm
             throw new IllegalArgumentException("Property type of a value cannot be undefined (" + LogUtil.safeGetJCRPath(this) + ").");
         }
 
-        // TODO -> internal set values to the property and remember operation
+        if (value == null) {
+            remove();
+        }
+        else {
+            parentState.setProperty(name, ValueConverter.toJsonValue(values));
+        }
     }
 
     /**
@@ -497,4 +609,5 @@ public class PropertyImpl extends ItemIm
             return value.getString().length();
         }
     }
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/RepositoryImpl.java Thu Mar 15 13:33:32 2012
@@ -39,8 +39,14 @@ public class RepositoryImpl extends Abst
      */
     private static final Logger log = LoggerFactory.getLogger(RepositoryImpl.class);
 
+    private final GlobalContext context;
+
     private Map<String, Value[]> descriptors;
 
+    public RepositoryImpl(GlobalContext context) {
+        this.context = context;
+    }
+
     //---------------------------------------------------------< Repository >---
     /**
      * @see javax.jcr.Repository#getDescriptorKeys()
@@ -102,8 +108,8 @@ public class RepositoryImpl extends Abst
      */
     @Override
     public Session login(Credentials credentials, String workspaceName) throws LoginException, NoSuchWorkspaceException, RepositoryException {
-        // TODO -> SPI
-        return null;
+        SessionFactory sessionFactory = context.getInstance(SessionFactory.class);
+        return sessionFactory.createSession(context, credentials, workspaceName);
     }
 
     //------------------------------------------------------------< private >---

Copied: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionContext.java (from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionContext.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionContext.java?p2=jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionContext.java&p1=jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionContext.java&r1=1299713&r2=1300973&rev=1300973&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionContext.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionContext.java Thu Mar 15 13:33:32 2012
@@ -17,11 +17,11 @@
  * under the License.
  */
 
-package org.apache.jackrabbit;
+package org.apache.jackrabbit.oak.jcr;
 
 import org.apache.jackrabbit.mk.api.MicroKernel;
-import org.apache.jackrabbit.security.CredentialsInfo;
-import org.apache.jackrabbit.state.NodeStateProvider;
+import org.apache.jackrabbit.oak.jcr.security.CredentialsInfo;
+import org.apache.jackrabbit.oak.jcr.state.NodeStateProvider;
 
 import javax.jcr.Session;
 import javax.jcr.ValueFactory;

Copied: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionFactory.java (from r1299713, jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionFactory.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionFactory.java?p2=jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionFactory.java&p1=jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionFactory.java&r1=1299713&r2=1300973&rev=1300973&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-microkernel/src/main/java/org/apache/jackrabbit/SessionFactory.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionFactory.java Thu Mar 15 13:33:32 2012
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package org.apache.jackrabbit;
+package org.apache.jackrabbit.oak.jcr;
 
 import javax.jcr.Credentials;
 import javax.jcr.LoginException;

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/SessionImpl.java Thu Mar 15 13:33:32 2012
@@ -17,6 +17,13 @@
 package org.apache.jackrabbit.oak.jcr;
 
 import org.apache.jackrabbit.commons.AbstractSession;
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.oak.jcr.security.Authenticator;
+import org.apache.jackrabbit.oak.jcr.security.CredentialsInfo;
+import org.apache.jackrabbit.oak.jcr.state.NodeStateProvider;
+import org.apache.jackrabbit.oak.jcr.state.TransientNodeState;
+import org.apache.jackrabbit.oak.jcr.state.TransientSpace;
+import org.apache.jackrabbit.oak.jcr.util.Path;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
@@ -27,6 +34,7 @@ import javax.jcr.InvalidItemStateExcepti
 import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.Node;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.ReferentialIntegrityException;
@@ -54,34 +62,122 @@ public class SessionImpl extends Abstrac
      */
     private static final Logger log = LoggerFactory.getLogger(SessionImpl.class);
 
-    private final Workspace workspace = null;
-    private final ValueFactory valueFactory = null;
+    private final Workspace workspace;
+    private final GlobalContext globalContext;
+    private final CredentialsInfo credentialsInfo;
+    private final String workspaceName;
+    private final MicroKernel microKernel;
+    private final TransientSpace transientSpace;
+    private final NodeStateProvider nodeStateProvider;
+
+    private String revision;
+    private boolean isAlive = true;
+
+    public static final SessionFactory FACTORY = new SessionFactory() {
+        @Override
+        public Session createSession(GlobalContext globalContext, Credentials credentials,
+                String workspaceName) throws LoginException, NoSuchWorkspaceException {
+
+            Authenticator authenticator = globalContext.getInstance(Authenticator.class);
+            CredentialsInfo credentialsInfo = authenticator.authenticate(credentials);
+            MicroKernel microKernel = globalContext.getInstance(MicroKernel.class);
+            String revision = microKernel.getHeadRevision();
+
+            if (workspaceName == null) {
+                workspaceName = WorkspaceImpl.DEFAULT_WORKSPACE_NAME;
+            }
+
+            if (!microKernel.nodeExists('/' + workspaceName, revision)) {
+                if (WorkspaceImpl.DEFAULT_WORKSPACE_NAME.equals(workspaceName)) {
+                    WorkspaceImpl.createWorkspace(microKernel, workspaceName);
+                    revision = microKernel.getHeadRevision();
+                }
+                else {
+                    throw new NoSuchWorkspaceException(workspaceName);
+                }
+            }
+
+            return new SessionImpl(globalContext, credentialsInfo, workspaceName, revision);
+        }
+    };
+
+    public interface Context extends SessionContext<SessionImpl>{}
+
+    private final Context sessionContext = new Context() {
+        @Override
+        public SessionImpl getSession() {
+            return SessionImpl.this;
+        }
+
+        @Override
+        public GlobalContext getGlobalContext() {
+            return globalContext;
+        }
+
+        @Override
+        public CredentialsInfo getCredentialsInfo() {
+            return credentialsInfo;
+        }
+
+        @Override
+        public String getWorkspaceName() {
+            return workspaceName;
+        }
+
+        @Override
+        public MicroKernel getMicrokernel() {
+            return microKernel;
+        }
+
+        @Override
+        public String getRevision() {
+            return revision;
+        }
+
+        @Override
+        public ValueFactory getValueFactory() {
+            return globalContext.getInstance(ValueFactory.class);
+        }
+
+        @Override
+        public NodeStateProvider getNodeStateProvider() {
+            return nodeStateProvider;
+        }
+    };
+
+    private SessionImpl(GlobalContext globalContext, CredentialsInfo credentialsInfo, String workspaceName,
+            String revision) {
+
+        this.globalContext = globalContext;
+        this.credentialsInfo = credentialsInfo;
+        this.workspaceName = workspaceName;
+        this.revision = revision;
+        workspace = new WorkspaceImpl(sessionContext);
+        microKernel = globalContext.getInstance(MicroKernel.class);
+        transientSpace = new TransientSpace(workspaceName, microKernel, revision);
+        nodeStateProvider = new NodeStateProvider(sessionContext, transientSpace);
+    }
 
-    private boolean isAlive;
 
     //------------------------------------------------------------< Session >---
     @Override
     public Repository getRepository() {
-        // TODO
-        return null;
+        return globalContext.getInstance(Repository.class);
     }
 
     @Override
     public String getUserID() {
-        // TODO
-        return null;
+        return credentialsInfo.getUserId();
     }
 
     @Override
     public String[] getAttributeNames() {
-        // TODO
-        return new String[0];
+        return credentialsInfo.getAttributeNames();
     }
 
     @Override
     public Object getAttribute(String name) {
-        // TODO
-        return null;
+        return credentialsInfo.getAttribute(name);
     }
 
     @Override
@@ -92,9 +188,7 @@ public class SessionImpl extends Abstrac
     @Override
     public Node getRootNode() throws RepositoryException {
         checkIsAlive();
-
-        // TODO
-        return null;
+        return NodeImpl.create(sessionContext, Path.create(workspaceName));
     }
 
     @Override
@@ -116,39 +210,39 @@ public class SessionImpl extends Abstrac
     @Override
     public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
         checkIsAlive();
+        Path sourcePath = Path.create(workspaceName, srcAbsPath);
+        TransientNodeState sourceParent = nodeStateProvider.getNodeState(sourcePath.getParent());
+        if (sourceParent == null) {
+            throw new PathNotFoundException(srcAbsPath);
+        }
 
-        // TODO
-
+        sourceParent.move(sourcePath.getName(), Path.create(workspaceName, destAbsPath));
     }
 
     @Override
     public void save() throws AccessDeniedException, ItemExistsException, ReferentialIntegrityException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
         checkIsAlive();
-
-        // TODO
-
+        revision = transientSpace.save();
+        nodeStateProvider.clear();
     }
 
     @Override
     public void refresh(boolean keepChanges) throws RepositoryException {
         checkIsAlive();
-
-        // TODO
-
+        revision = transientSpace.refresh(keepChanges);
+        nodeStateProvider.clear();
     }
 
     @Override
     public boolean hasPendingChanges() throws RepositoryException {
         checkIsAlive();
-
-        // TODO
-        return false;
+        return transientSpace.isDirty();
     }
 
     @Override
     public ValueFactory getValueFactory() throws UnsupportedRepositoryOperationException, RepositoryException {
         checkIsAlive();
-        return valueFactory;
+        return sessionContext.getValueFactory();
     }
 
     @Override
@@ -198,6 +292,7 @@ public class SessionImpl extends Abstrac
             return;
         }
 
+        isAlive = false;
         // TODO
     }
 

Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java?rev=1300973&r1=1300972&r2=1300973&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/WorkspaceImpl.java Thu Mar 15 13:33:32 2012
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.jcr;
 
+import org.apache.jackrabbit.mk.api.MicroKernel;
+import org.apache.jackrabbit.oak.jcr.SessionImpl.Context;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.ContentHandler;
@@ -48,24 +50,28 @@ import java.io.InputStream;
  * <code>WorkspaceImpl</code>...
  */
 public class WorkspaceImpl implements Workspace {
+    public static final String DEFAULT_WORKSPACE_NAME = "default";
 
     /**
      * logger instance
      */
     private static final Logger log = LoggerFactory.getLogger(WorkspaceImpl.class);
 
-    private final SessionImpl session = null;
-    private final String name = null;
+    private final Context sessionContext;
+
+    public WorkspaceImpl(Context sessionContext) {
+        this.sessionContext = sessionContext;
+    }
 
     //----------------------------------------------------------< Workspace >---
     @Override
     public Session getSession() {
-        return session;
+        return sessionContext.getSession();
     }
 
     @Override
     public String getName() {
-        return name;
+        return sessionContext.getWorkspaceName();
     }
 
     @Override
@@ -75,8 +81,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void copy(String srcWorkspace, String srcAbsPath, String destAbsPath) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
-        session.checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO -> SPI
 
@@ -84,8 +90,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void clone(String srcWorkspace, String srcAbsPath, String destAbsPath, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
-        session.checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO -> SPI
 
@@ -93,8 +99,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void move(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
-        session.checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO -> SPI
 
@@ -107,8 +113,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public LockManager getLockManager() throws UnsupportedRepositoryOperationException, RepositoryException {
-        session.checkIsAlive();
-        session.checkSupportedOption(Repository.OPTION_LOCKING_SUPPORTED);
+        getOakSession().checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_LOCKING_SUPPORTED);
 
         // TODO
         return null;
@@ -116,7 +122,7 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public QueryManager getQueryManager() throws RepositoryException {
-        session.checkIsAlive();
+        getOakSession().checkIsAlive();
 
         // TODO
         return null;
@@ -124,7 +130,7 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public NamespaceRegistry getNamespaceRegistry() throws RepositoryException {
-        session.checkIsAlive();
+        getOakSession().checkIsAlive();
 
         // TODO
         return null;
@@ -132,7 +138,7 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public NodeTypeManager getNodeTypeManager() throws RepositoryException {
-        session.checkIsAlive();
+        getOakSession().checkIsAlive();
 
         // TODO
         return null;
@@ -140,8 +146,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public ObservationManager getObservationManager() throws UnsupportedRepositoryOperationException, RepositoryException {
-        session.checkSupportedOption(Repository.OPTION_OBSERVATION_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_OBSERVATION_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO
         return null;
@@ -149,8 +155,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public VersionManager getVersionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
-        session.checkIsAlive();
-        session.checkSupportedOption(Repository.OPTION_VERSIONING_SUPPORTED);
+        getOakSession().checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_VERSIONING_SUPPORTED);
 
         // TODO
         return null;
@@ -158,7 +164,7 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public String[] getAccessibleWorkspaceNames() throws RepositoryException {
-        session.checkIsAlive();
+        getOakSession().checkIsAlive();
 
         // TODO
         return new String[0];
@@ -166,8 +172,8 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, AccessDeniedException, RepositoryException {
-        session.checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO
         return null;
@@ -175,35 +181,47 @@ public class WorkspaceImpl implements Wo
 
     @Override
     public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, VersionException, PathNotFoundException, ItemExistsException, ConstraintViolationException, InvalidSerializedDataException, LockException, AccessDeniedException, RepositoryException {
-        session.checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
-        session.checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.LEVEL_2_SUPPORTED);
+        getOakSession().checkIsAlive();
 
         // TODO -> SPI
     }
 
     @Override
     public void createWorkspace(String name) throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
-        session.checkIsAlive();
-        session.checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
-        // TODO -> SPI
+        getOakSession().checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
+
+        createWorkspace(sessionContext.getMicrokernel(), name);
     }
 
     @Override
     public void createWorkspace(String name, String srcWorkspace) throws AccessDeniedException, UnsupportedRepositoryOperationException, NoSuchWorkspaceException, RepositoryException {
-        session.checkIsAlive();
-        session.checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
+        getOakSession().checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
 
         // TODO -> SPI
     }
 
     @Override
     public void deleteWorkspace(String name) throws AccessDeniedException, UnsupportedRepositoryOperationException, NoSuchWorkspaceException, RepositoryException {
-        session.checkIsAlive();
-        session.checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
+        getOakSession().checkIsAlive();
+        getOakSession().checkSupportedOption(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED);
 
-        // TODO -> SPI
+        MicroKernel microKernel = sessionContext.getMicrokernel();
+        String revision = microKernel.getHeadRevision();
+        microKernel.commit("/", "- \"" + name + '\"', revision, null);
     }
 
     //------------------------------------------------------------< private >---
+    
+    private SessionImpl getOakSession() {
+        return sessionContext.getSession();
+    }
+
+    static void createWorkspace(MicroKernel microKernel,String name) {
+        String revision = microKernel.getHeadRevision();
+        microKernel.commit("/", "+ \"" + name + "\" : {}", revision, null);
+    }
 
 }
\ No newline at end of file

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/OakRepositoryConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/OakRepositoryConfiguration.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/OakRepositoryConfiguration.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/OakRepositoryConfiguration.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.jcr.configuration;
+
+import javax.jcr.RepositoryException;
+import java.util.Collections;
+import java.util.Map;
+
+import static java.text.MessageFormat.format;
+
+public class OakRepositoryConfiguration implements RepositoryConfiguration {
+    private final Map<String, String> parameters;
+    private final String microkernelUrl;
+    private final int nodeStateCacheSize;
+
+    private OakRepositoryConfiguration(Map<String, String> parameters) throws RepositoryException {
+        this.parameters = Collections.unmodifiableMap(parameters);
+
+        microkernelUrl = getParameterMap().get(MICROKERNEL_URL);
+        if (microkernelUrl == null) {
+            throw new RepositoryException(format("Missing configuration value for {0}", MICROKERNEL_URL));
+        }
+
+        String size = getParameterMap().get(NODE_STATE_CACHE_SIZE);
+        try {
+            nodeStateCacheSize = size == null ? 0 : Integer.parseInt(size);
+        }
+        catch (NumberFormatException e) {
+            throw new RepositoryException(format("Invalid configuration value {0} for {1}",
+                    size, NODE_STATE_CACHE_SIZE), e);
+        }
+
+    }
+
+    public static RepositoryConfiguration create(Map<String, String> parameters) throws RepositoryException {
+        return new OakRepositoryConfiguration(parameters);
+    }
+
+    @Override
+    public final Map<String, String> getParameterMap() {
+        return parameters;
+    }
+
+    @Override
+    public String getMicrokernelUrl() throws RepositoryException {
+        return microkernelUrl;
+    }
+
+    @Override
+    public int getNodeStateCacheSize() {
+        return nodeStateCacheSize;
+    }
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/RepositoryConfiguration.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/RepositoryConfiguration.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/RepositoryConfiguration.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/configuration/RepositoryConfiguration.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,32 @@
+/*
+ * 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.jcr.configuration;
+
+import javax.jcr.RepositoryException;
+import java.util.Map;
+
+public interface RepositoryConfiguration {
+    String MICROKERNEL_URL = RepositoryConfiguration.class.getName() + ".microkernel-url";
+    String NODE_STATE_CACHE_SIZE = RepositoryConfiguration.class.getName() + ".node-state-cache-size";
+
+    Map<String, String> getParameterMap();
+    String getMicrokernelUrl() throws RepositoryException;
+    int getNodeStateCacheSize();
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/DefaultJsonTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/DefaultJsonTokenizer.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/DefaultJsonTokenizer.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/DefaultJsonTokenizer.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,214 @@
+/*
+ * 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.jcr.json;
+
+import org.apache.jackrabbit.oak.jcr.json.Token.Type;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This JSON tokenizer operates on a string as its input. For maximal performance
+ * it <em>does not</em> unescape JSON string values.
+ * Use {@link JsonValue#unescape(String)} to unescape the text of {@link Token}s
+ * of type {@link Type#STRING}.
+ * 
+ * @see UnescapingJsonTokenizer
+ */
+public class DefaultJsonTokenizer extends JsonTokenizer {
+    private final String json;
+
+    private int pos;
+
+    /**
+     * Create a tokenizer for the given input string
+     * @param json
+     */
+    public DefaultJsonTokenizer(String json) {
+        this.json = json;
+    }
+
+    /**
+     * @see JsonTokenizer#JsonTokenizer(JsonTokenizer)
+     */
+    protected DefaultJsonTokenizer(DefaultJsonTokenizer tokenizer) {
+        super(tokenizer);
+        json = tokenizer.json;
+        pos = tokenizer.pos;
+    }
+
+    @Override
+    protected Token nextToken() {
+        skipWhiteSpace();
+        if (pos >= json.length()) {
+            return createToken(Type.EOF, "", pos);
+        }
+
+        switch (json.charAt(pos)) {
+            case '{': return createToken(Type.BEGIN_OBJECT, "{", pos++);
+            case '}': return createToken(Type.END_OBJECT, "}", pos++);
+            case '[': return createToken(Type.BEGIN_ARRAY, "[", pos++);
+            case ']': return createToken(Type.END_ARRAY, "]", pos++);
+            case ':': return createToken(Type.COLON, ":", pos++);
+            case ',': return createToken(Type.COMMA, ",", pos++);
+            case 't': return readLiteral(Type.TRUE, "true");
+            case 'f': return readLiteral(Type.FALSE, "false");
+            case 'n': return readLiteral(Type.NULL, "null");
+            case '"': return readString();
+            default:  return isNumber() ? readNumber() : readUnknown();
+        }
+    }
+
+    @Override
+    public int pos() {
+        return peek().pos();
+    }
+
+    @Override
+    public void setPos(int pos) {
+        currentToken = null;
+        this.pos = pos;
+    }
+
+    @Override
+    public String toString() {
+        return (currentToken == null ? "" : currentToken) + " " + json.substring(pos);
+    }
+
+    @Override
+    public DefaultJsonTokenizer copy() {
+        return new DefaultJsonTokenizer(this);
+    }
+
+    //------------------------------------------< protected >---
+
+    /**
+     * Advance {@link #pos()} until the current character is not a
+     * whitespace character.
+     */
+    protected void skipWhiteSpace() {
+        while (pos < json.length() && Character.isWhitespace(json.charAt(pos))) {
+            pos++;
+        }
+    }
+
+    /**
+     * Factory method for creating {@link Token}s
+     * @param type
+     * @param text
+     * @param pos
+     * @return a new token
+     */
+    protected Token createToken(Type type, String text, int pos) {
+        return new Token(type, text, pos);
+    }
+
+    /**
+     * Read the literal {@code text} and create a token of the given {@code type}
+     * @param type
+     * @param text
+     * @return a new token
+     * @throws ParseException  if {@code text} cannot be read at the current position
+     */
+    protected Token readLiteral(Type type, String text) {
+        if (json.substring(pos).startsWith(text)) {
+            Token token = createToken(type, text, pos);
+            pos += text.length();
+            return token;
+        }
+        else {
+            throw new ParseException(pos, "Expected '" + text + ",' found: " + excerpt(json, pos, 40));
+        }
+    }
+
+    /**
+     * Read a JSON string and create a {@link Token.Type#STRING} token.
+     * @return a new token
+     * @throws ParseException  if no string can be read at the current position
+     */
+    protected Token readString() {
+        int i;
+        boolean found = false;
+        boolean even = true;
+
+        // starting at pos + 1, find index i of the first quote character in json which
+        // is preceded by an even number of backslash characters
+        for (i = pos + 1; i < json.length() && !(found = json.charAt(i) == '"' && even); i++) {
+            even = json.charAt(i) != '\\' || !even;
+        }
+
+        if (found) {
+            Token token = createToken(Type.STRING, json.substring(pos + 1, i), pos);
+            pos = i + 1;
+            return token;
+        }
+        else {
+            throw new ParseException(pos, "Expected string, found. " + excerpt(json, pos, 40));
+        }
+    }
+    
+    private static final Pattern NUMBER_PATTERN = Pattern.compile(
+            "(\\+|-)?(\\d+)((\\.)(\\d+))?(((e|E)(\\+|-)?)(\\d+))?");
+
+    /**
+     * Read a JSON number and create a {@link Token.Type#NUMBER} token.
+     * @return a new token
+     * @throws ParseException  if no number can be read at the current position
+     */
+    protected Token readNumber() {
+        Matcher matcher = NUMBER_PATTERN.matcher(json.substring(pos));
+        if (matcher.lookingAt()) {
+            Token token = createToken(Type.NUMBER, matcher.group(), pos);
+            pos += matcher.end();
+            return token;
+        }
+        else {
+            throw new ParseException(pos, "Expected number, found. " + excerpt(json, pos, 40));
+        }
+    }
+
+    /**
+     * Read from the current position until a new token starts and create a
+     * {@link Token.Type#UNKNOWN} token.
+     * @return a new token
+     */
+    protected Token readUnknown() {
+        int start = pos++;
+        while (pos < json.length() && "{}[]:,tfn+-0123456789\" ".indexOf(json.charAt(pos)) == -1)  {
+            pos++;
+        }
+        return new Token(Type.UNKNOWN, json.substring(start, pos), start);
+    }
+
+    //------------------------------------------< private >---
+
+    private boolean isNumber() {
+        // true if first character is a digit or a sign and second character is a digit
+        char first = json.charAt(pos);
+        return !(!Character.isDigit(first) &&
+                ('+' != first && '-' != first || pos + 1 >= json.length() ||
+                 !Character.isDigit(json.charAt(pos + 1))));
+    }
+
+    private static String excerpt(String string, int pos, int len) {
+        return string.substring(pos, Math.min(string.length(), pos + len)) + "...";
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/FullJsonParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/FullJsonParser.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/FullJsonParser.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/FullJsonParser.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,117 @@
+/*
+ * 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.jcr.json;
+
+import org.apache.jackrabbit.oak.jcr.json.JsonValue.JsonArray;
+import org.apache.jackrabbit.oak.jcr.json.JsonValue.JsonAtom;
+import org.apache.jackrabbit.oak.jcr.json.JsonValue.JsonObject;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+
+/**
+ * Utility class for parsing JSON objects and arrays into
+ * {@link JsonObject}s and {@link JsonArray}s, respectively.
+ *
+ * @see LevelOrderJsonParser
+ */
+public class FullJsonParser {
+    private FullJsonParser() { }
+
+    /**
+     * Parse a JSON object from {@code tokenizer}
+     * @param tokenizer
+     * @return a {@code JsonObject}
+     * @throws ParseException
+     */
+    public static JsonObject parseObject(JsonTokenizer tokenizer) {
+        ObjectHandler objectHandler = new ObjectHandler();
+        new JsonParser(objectHandler).parseObject(tokenizer);
+        return objectHandler.getObject();
+    }
+
+    /**
+     * Parse a JSON array from {@code tokenizer}
+     * @param tokenizer
+     * @return a {@code JsonArray}
+     * @throws ParseException
+     */
+    public static JsonArray parseArray(JsonTokenizer tokenizer) {
+        ArrayHandler arrayHandler = new ArrayHandler();
+        new JsonParser(arrayHandler).parseArray(tokenizer);
+        return arrayHandler.getArray();
+    }
+
+    /**
+     * This implementation of a {@code JsonHandler} builds up a {@code JsonObject}
+     * by recursively descending into its constituents.  
+     */
+    public static class ObjectHandler extends JsonHandler {
+        private final JsonObject object = new JsonObject(new LinkedHashMap<String, JsonValue>());
+
+        @Override
+        public void atom(Token key, Token value) {
+            object.put(key.text(), new JsonAtom(value));
+        }
+
+        @Override
+        public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+            object.put(key.text(), parseObject(tokenizer));
+        }
+
+        @Override
+        public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+            object.put(key.text(), parseArray(tokenizer));
+        }
+
+        public JsonObject getObject() {
+            return object;
+        }
+
+    }
+
+    /**
+     * This implementation of a {@code JsonHandler} builds up a {@code JsonArray}
+     * by recursively descending into its constituents.
+     */
+    public static class ArrayHandler extends JsonHandler {
+        private final JsonArray array = new JsonArray(new ArrayList<JsonValue>());
+
+        @Override
+        public void atom(Token key, Token value) {
+            array.add(new JsonAtom(value));
+        }
+
+        @Override
+        public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+            array.add(parseObject(tokenizer));
+        }
+
+        @Override
+        public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+            array.add(parseArray(tokenizer));
+        }
+
+        public JsonArray getArray() {
+            return array;
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonHandler.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonHandler.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonHandler.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,84 @@
+/*
+ * 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.jcr.json;
+
+/**
+ * Handler for semantic actions of a {@link JsonParser}.
+ * This class provides a handler which fully parses a JSON
+ * document by recursive decent without executing any actions.
+ * <p/>
+ * Override this class to add semantic actions as needed. 
+ */
+public class JsonHandler {
+
+    /**
+     * Default instance which can be used to skip any part of a
+     * JSON document. 
+     */
+    public static final JsonHandler INSTANCE = new JsonHandler();
+
+    /**
+     * A primitive JSON value (ATOM) has been parsed.
+     * @param key
+     * @param value
+     */
+    public void atom(Token key, Token value) { }
+
+    /**
+     * A COMMA has been parsed
+     * @param token
+     */
+    public void comma(Token token) { }
+
+    /**
+     * Parser PAIR. This implementation simply delegates back
+     * to {@link JsonParser#parsePair(JsonTokenizer)}
+     * 
+     * @param parser
+     * @param tokenizer
+     */
+    public void pair(JsonParser parser, JsonTokenizer tokenizer) {
+        parser.parsePair(tokenizer);
+    }
+
+    /**
+     * Parser OBJECT. This implementation simply delegates back
+     * to {@link JsonParser#parseObject(JsonTokenizer)}
+     *
+     * @param parser
+     * @param key
+     * @param tokenizer
+     */
+    public void object(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+        parser.parseObject(tokenizer);
+    }
+
+    /**
+     * Parser ARRAY. This implementation simply delegates back
+     * to {@link JsonParser#parseArray(JsonTokenizer)} 
+     *
+     * @param parser
+     * @param key
+     * @param tokenizer
+     */
+    public void array(JsonParser parser, Token key, JsonTokenizer tokenizer) {
+        parser.parseArray(tokenizer);
+    }
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonParser.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonParser.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonParser.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonParser.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,172 @@
+/*
+ * 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.jcr.json;
+
+
+import org.apache.jackrabbit.oak.jcr.json.Token.Type;
+
+/**
+ * A parser for the JSON format accepting the following grammar:
+ *
+ * <pre>
+ * OBJECT    ::= { (PAIR (, PAIR)*)? }
+ * PAIR      ::= STRING : VALUE
+ * VALUE     ::= OBJECT | ARRAY | STRING | NUMBER | true | false | null
+ * ARRAY     ::= [ (VALUE (, VALUE)*)? ]
+ * </pre>
+ *
+ * Semantic actions are attached through a {@link JsonHandler} instance
+ * which is passed to the constructor. For each of the above productions
+ * the parser provides a corresponding method which take a {@link JsonTokenizer}
+ * for reading the JSON input. These methods call the respective call back on the
+ * {@code JsonHandler} for each of the constituents of the production.
+ * <p/>
+ * Note: In contrast to conventional parsers, this parser <em>does not</em>
+ * recursively decent into nested structures (OBJECT, ARRAY and PAIR, that is).
+ * Instead it calls the respective method on the {@code JsonHandler} which
+ * is can use this parser instance or any other parser to continue parsing.
+ *
+ * @see <a href="http://www.json.org/">json.org</a>
+ */
+public final class JsonParser {
+    private final JsonHandler jsonHandler;
+
+    public JsonParser(JsonHandler jsonHandler) {
+        this.jsonHandler = jsonHandler;
+    }
+
+    /**
+     * Parses
+     * <pre>
+     * OBJECT ::= { (PAIR (, PAIR)*)? }
+     * </pre>
+     * Calls {@link JsonHandler#comma(Token)}
+     * @param tokenizer
+     * @throws ParseException
+     */
+    public void parseObject(JsonTokenizer tokenizer) {
+        tokenizer.read(Type.BEGIN_OBJECT);
+
+        if (tryParsePair(tokenizer)) {
+            while (tokenizer.peek(Type.COMMA)) {
+                jsonHandler.comma(tokenizer.read());
+                if (!tryParsePair(tokenizer)) {
+                    throw new ParseException(tokenizer.pos(),  "Expected pair, found: " + tokenizer.peek());
+                }
+            }
+        }
+        tokenizer.read(Type.END_OBJECT);
+    }
+
+    /**
+     * Parses
+     * <pre>
+     * PAIR ::= STRING: VALUE
+     * </pre>
+     * @param tokenizer
+     * @throws ParseException
+     */
+    public void parsePair(JsonTokenizer tokenizer) {
+        if (!tokenizer.peek(Type.STRING)) {
+            throw new ParseException(tokenizer.pos(), "Expected string, found: " + tokenizer.peek());
+        }
+
+        Token key = tokenizer.read();
+        tokenizer.read(Type.COLON);
+        parseValue(key, tokenizer);
+    }
+
+    /**
+     * Parses
+     * <pre>
+     * VALUE ::= OBJECT | ARRAY | STRING | NUMBER | true | false | null
+     * </pre>
+     * Calls one of {@link JsonHandler#object(JsonParser, Token, JsonTokenizer)},
+     * {@link JsonHandler#array(JsonParser, Token, JsonTokenizer)} and
+     * {@link JsonHandler#atom(Token, Token)}
+     * @param tokenizer
+     * @throws ParseException
+     */
+    public void parseValue(Token key, JsonTokenizer tokenizer) {
+        switch (tokenizer.peek().type()) {
+            case BEGIN_OBJECT:
+                jsonHandler.object(this, key, tokenizer);
+                break;
+            case BEGIN_ARRAY:
+                jsonHandler.array(this, key, tokenizer);
+                break;
+            case STRING:
+            case NUMBER:
+            case TRUE:
+            case FALSE:
+            case NULL:
+                jsonHandler.atom(key, tokenizer.read());
+                break;
+            default:
+                throw new ParseException(tokenizer.pos(), "Expected value, found: " + tokenizer.peek());
+        }
+    }
+
+    /**
+     * Parses
+     * <pre>
+     * ARRAY ::= [ (VALUE (, VALUE)*)? ]
+     * </pre>
+     * Calls {@link JsonHandler#comma(Token)}
+     * @param tokenizer
+     * @throws ParseException
+     */
+    public void parseArray(JsonTokenizer tokenizer) {
+        tokenizer.read(Type.BEGIN_ARRAY);
+
+        if (tryParseValue(tokenizer)) {
+            while (tokenizer.peek(Type.COMMA)) {
+                jsonHandler.comma(tokenizer.read());
+                if (!tryParseValue(tokenizer)) {
+                    throw new ParseException(tokenizer.pos(), "Expected value, found: " + tokenizer.peek()); 
+                }
+            }
+        }
+        tokenizer.read(Type.END_ARRAY);
+    }
+
+    //------------------------------------------< private >---
+    
+    private boolean tryParsePair(JsonTokenizer tokenizer) {
+        if (tokenizer.peek(Type.STRING)) {
+            jsonHandler.pair(this, tokenizer);
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    private boolean tryParseValue(JsonTokenizer tokenizer) {
+        if (tokenizer.peek(Type.END_ARRAY)) {
+            return false;
+        }
+        else {
+            parseValue(null, tokenizer);
+            return true;
+        }
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonTokenizer.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonTokenizer.java?rev=1300973&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonTokenizer.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/json/JsonTokenizer.java Thu Mar 15 13:33:32 2012
@@ -0,0 +1,138 @@
+/*
+ * 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.jcr.json;
+
+import org.apache.jackrabbit.oak.jcr.json.Token.Type;
+
+/**
+ * Abstract base class for JSON tokenizers.
+ * A JSON tokenizer breaks a stream of character into {@link Token}s. It has
+ * a current {@link #pos() position} and methods for inspecting, reading and
+ * skipping the token at the current position.
+ */
+public abstract class JsonTokenizer {
+
+    /**
+     * The current token which has been read ahead, if any.
+     * {@code null} otherwise.
+     */
+    protected Token currentToken;
+
+    /**
+     * Copy constructor. To be used in conjunction with {@link #copy()}
+     * @param tokenizer
+     */
+    protected JsonTokenizer(JsonTokenizer tokenizer) {
+        currentToken = tokenizer.currentToken;
+    }
+
+    protected JsonTokenizer() { }
+
+    /**
+     * Returns the current token without advancing the {@link #pos() position}
+     * @return  current token
+     */
+    public Token peek() {
+        if (currentToken == null) {
+            currentToken = nextToken();
+        }
+
+        return currentToken;
+    }
+
+    /**
+     * @param type
+     * @return  {@code true} if and only if the current token is of the given {@code type}
+     */
+    public boolean peek(Type type) {
+        return peek().type() == type;
+    }
+
+    /**
+     * Returns the current token and advances the {@link #pos() position}
+     * @return  current token
+     */
+    public Token read() {
+        if (currentToken == null) {
+            return nextToken();
+        }
+        else {
+            Token token = currentToken;
+            currentToken = null;
+            return token;
+        }
+    }
+
+    /**
+     * Returns the current token and advances the {@link #pos() position} if the token
+     * is of the given {@code type}.
+     * @param type
+     * @return  current token
+     * @throws ParseException  if the token is not of the given {@code type}.
+     */
+    public Token read(Type type) {
+        Token token = peek();
+        if (token.type() == type) {
+            return read();
+        }
+        else {
+            throw new ParseException(token.pos(), "Expected token type " + type + ", found: " + token); 
+        }
+    }
+
+    /**
+     * Advances the {@link #pos() position} if the token is of the given {@code type}.
+     * @param type
+     * @return  {@code true} if and only if the is token is of the given {@code type}.
+     */
+    public boolean skip(Type type) {
+        if (peek(type)) {
+            read();
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * @return the current position
+     */
+    public abstract int pos();
+
+    /**
+     * Set the current position
+     * @param pos
+     */
+    public abstract void setPos(int pos);
+
+    /**
+     * Create a copy of this tokenizer with the same state. Implementations usually
+     * create a new instance by calling the (overriden) {@link #JsonTokenizer(JsonTokenizer) copy constructor}.
+     * @return copy of this tokenizer
+     */
+    public abstract JsonTokenizer copy();
+
+    /**
+     * Read the next token from the input and advance the current {@link #pos() positon}.
+     * @return  next token
+     */
+    protected abstract Token nextToken();
+}