You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ju...@apache.org on 2014/05/19 13:57:27 UTC

svn commit: r1595852 [2/2] - in /jackrabbit/oak/branches/1.0: ./ oak-core/src/main/java/org/apache/jackrabbit/oak/api/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/memory/ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/ oa...

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/memory/MemoryStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/memory/MemoryStore.java?rev=1595852&r1=1595851&r2=1595852&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/memory/MemoryStore.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/memory/MemoryStore.java Mon May 19 11:57:26 2014
@@ -1,128 +1,128 @@
-/*
- * 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.plugins.segment.memory;
-
-import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.ConcurrentMap;
-
-import javax.annotation.Nonnull;
-
-import org.apache.jackrabbit.oak.api.Blob;
-import org.apache.jackrabbit.oak.plugins.segment.Segment;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentTracker;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentStore;
-import org.apache.jackrabbit.oak.plugins.segment.SegmentWriter;
-import org.apache.jackrabbit.oak.spi.blob.BlobStore;
-import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
-import org.apache.jackrabbit.oak.spi.state.NodeState;
-
-import com.google.common.collect.Maps;
-
-public class MemoryStore implements SegmentStore {
-
-    private final SegmentTracker tracker = new SegmentTracker(this);
-
-    private SegmentNodeState head;
-
-    private final ConcurrentMap<SegmentId, Segment> segments =
-            Maps.newConcurrentMap();
-
-    public MemoryStore(NodeState root) {
-        NodeBuilder builder = EMPTY_NODE.builder();
-        builder.setChildNode("root", root);
-
-        SegmentWriter writer = tracker.getWriter();
-        this.head = writer.writeNode(builder.getNodeState());
-        writer.flush();
-    }
-
-    public MemoryStore() {
-        this(EMPTY_NODE);
-    }
-
-    @Override
-    public SegmentTracker getTracker() {
-        return tracker;
-    }
-
-    @Override
-    public synchronized SegmentNodeState getHead() {
-        return head;
-    }
-
-    @Override
-    public synchronized boolean setHead(SegmentNodeState base, SegmentNodeState head) {
-        if (this.head.getRecordId().equals(base.getRecordId())) {
-            this.head = head;
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public boolean containsSegment(SegmentId id) {
-        return id.getTracker() == tracker || segments.containsKey(id);
-    }
-
-    @Override @Nonnull
-    public Segment readSegment(SegmentId id) {
-        Segment segment = segments.get(id);
-        if (segment != null) {
-            return segment;
-        } else {
-            throw new IllegalArgumentException("Segment not found: " + id);
-        }
-    }
-
-    @Override
-    public void writeSegment(
-            SegmentId id, byte[] data, int offset, int length) {
-        ByteBuffer buffer = ByteBuffer.allocate(length);
-        buffer.put(data, offset, length);
-        buffer.rewind();
-        Segment segment = new Segment(tracker, id, buffer);
-        if (segments.putIfAbsent(id, segment) != null) {
-            throw new IllegalStateException("Segment override: " + id);
-        }
-    }
-
-    @Override
-    public void close() {
-    }
-
-    @Override
-    public Blob readBlob(String reference) {
-        return null;
-    }
-
-    @Override
-    public BlobStore getBlobStore() {
-        return null;
-    }
-
-    @Override
-    public void gc() {
-        System.gc();
-        segments.keySet().retainAll(tracker.getReferencedSegmentIds());
-    }
-
-}
+/*
+ * 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.plugins.segment.memory;
+
+import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ConcurrentMap;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.plugins.segment.Segment;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentId;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentTracker;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentStore;
+import org.apache.jackrabbit.oak.plugins.segment.SegmentWriter;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import com.google.common.collect.Maps;
+
+public class MemoryStore implements SegmentStore {
+
+    private final SegmentTracker tracker = new SegmentTracker(this);
+
+    private SegmentNodeState head;
+
+    private final ConcurrentMap<SegmentId, Segment> segments =
+            Maps.newConcurrentMap();
+
+    public MemoryStore(NodeState root) {
+        NodeBuilder builder = EMPTY_NODE.builder();
+        builder.setChildNode("root", root);
+
+        SegmentWriter writer = tracker.getWriter();
+        this.head = writer.writeNode(builder.getNodeState());
+        writer.flush();
+    }
+
+    public MemoryStore() {
+        this(EMPTY_NODE);
+    }
+
+    @Override
+    public SegmentTracker getTracker() {
+        return tracker;
+    }
+
+    @Override
+    public synchronized SegmentNodeState getHead() {
+        return head;
+    }
+
+    @Override
+    public synchronized boolean setHead(SegmentNodeState base, SegmentNodeState head) {
+        if (this.head.getRecordId().equals(base.getRecordId())) {
+            this.head = head;
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean containsSegment(SegmentId id) {
+        return id.getTracker() == tracker || segments.containsKey(id);
+    }
+
+    @Override @Nonnull
+    public Segment readSegment(SegmentId id) {
+        Segment segment = segments.get(id);
+        if (segment != null) {
+            return segment;
+        } else {
+            throw new IllegalArgumentException("Segment not found: " + id);
+        }
+    }
+
+    @Override
+    public void writeSegment(
+            SegmentId id, byte[] data, int offset, int length) {
+        ByteBuffer buffer = ByteBuffer.allocate(length);
+        buffer.put(data, offset, length);
+        buffer.rewind();
+        Segment segment = new Segment(tracker, id, buffer);
+        if (segments.putIfAbsent(id, segment) != null) {
+            throw new IllegalStateException("Segment override: " + id);
+        }
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public Blob readBlob(String reference) {
+        return null;
+    }
+
+    @Override
+    public BlobStore getBlobStore() {
+        return null;
+    }
+
+    @Override
+    public void gc() {
+        System.gc();
+        segments.keySet().retainAll(tracker.getReferencedSegmentIds());
+    }
+
+}

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/BinaryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/BinaryImpl.java?rev=1595852&r1=1595851&r2=1595852&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/BinaryImpl.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/BinaryImpl.java Mon May 19 11:57:26 2014
@@ -1,108 +1,108 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.jackrabbit.oak.plugins.value;
-
-import static com.google.common.base.Objects.toStringHelper;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.annotation.CheckForNull;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-
-import com.google.common.base.Objects;
-import org.apache.jackrabbit.api.ReferenceBinary;
-
-/**
- * TODO document
- */
-class BinaryImpl implements ReferenceBinary {
-
-    private final ValueImpl value;
-
-    BinaryImpl(ValueImpl value) {
-        this.value = value;
-    }
-
-    ValueImpl getBinaryValue() {
-        return value.getType() == PropertyType.BINARY ? value : null;
-    }
-
-    //-------------------------------------------------------------< Binary >---
-
-    @Override
-    public InputStream getStream() {
-        return value.getBlob().getNewStream();
-    }
-
-    @Override
-    public int read(byte[] b, long position) throws IOException {
-        InputStream stream = getStream();
-        try {
-            if (position != stream.skip(position)) {
-                throw new IOException("Can't skip to position " + position);
-            }
-            return stream.read(b);
-        } finally {
-            stream.close();
-        }
-    }
-
-    @Override
-    public long getSize() throws RepositoryException {
-        switch (value.getType()) {
-        case PropertyType.NAME:
-        case PropertyType.PATH:
-            // need to respect namespace remapping
-            return value.getString().length();
-        default:
-            return value.getBlob().length();
-        }
-    }
-
-    @Override
-    public void dispose() {
-        // nothing to do
-    }
-
-    //---------------------------------------------------< ReferenceBinary >--
-
-    @Override @CheckForNull
-    public String getReference() {
-        return value.getBlob().getReference();
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        if (other instanceof ReferenceBinary) {
-            return Objects.equal(getReference(), ((ReferenceBinary) other).getReference());
-        } else {
-            return false;
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(getReference());
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this).addValue(value).toString();
-    }
+/*
+ * 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.plugins.value;
+
+import static com.google.common.base.Objects.toStringHelper;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.annotation.CheckForNull;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import com.google.common.base.Objects;
+import org.apache.jackrabbit.api.ReferenceBinary;
+
+/**
+ * TODO document
+ */
+class BinaryImpl implements ReferenceBinary {
+
+    private final ValueImpl value;
+
+    BinaryImpl(ValueImpl value) {
+        this.value = value;
+    }
+
+    ValueImpl getBinaryValue() {
+        return value.getType() == PropertyType.BINARY ? value : null;
+    }
+
+    //-------------------------------------------------------------< Binary >---
+
+    @Override
+    public InputStream getStream() {
+        return value.getBlob().getNewStream();
+    }
+
+    @Override
+    public int read(byte[] b, long position) throws IOException {
+        InputStream stream = getStream();
+        try {
+            if (position != stream.skip(position)) {
+                throw new IOException("Can't skip to position " + position);
+            }
+            return stream.read(b);
+        } finally {
+            stream.close();
+        }
+    }
+
+    @Override
+    public long getSize() throws RepositoryException {
+        switch (value.getType()) {
+        case PropertyType.NAME:
+        case PropertyType.PATH:
+            // need to respect namespace remapping
+            return value.getString().length();
+        default:
+            return value.getBlob().length();
+        }
+    }
+
+    @Override
+    public void dispose() {
+        // nothing to do
+    }
+
+    //---------------------------------------------------< ReferenceBinary >--
+
+    @Override @CheckForNull
+    public String getReference() {
+        return value.getBlob().getReference();
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof ReferenceBinary) {
+            return Objects.equal(getReference(), ((ReferenceBinary) other).getReference());
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getReference());
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this).addValue(value).toString();
+    }
 }
\ No newline at end of file

Modified: jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/ValueImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/ValueImpl.java?rev=1595852&r1=1595851&r2=1595852&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/ValueImpl.java (original)
+++ jackrabbit/oak/branches/1.0/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/value/ValueImpl.java Mon May 19 11:57:26 2014
@@ -1,306 +1,306 @@
-/*
- * 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.plugins.value;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
-
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.util.Calendar;
-
-import javax.jcr.Binary;
-import javax.jcr.PropertyType;
-import javax.jcr.RepositoryException;
-import javax.jcr.Value;
-import javax.jcr.ValueFormatException;
-
-import com.google.common.base.Objects;
-import org.apache.jackrabbit.api.JackrabbitValue;
-import org.apache.jackrabbit.oak.api.Blob;
-import org.apache.jackrabbit.oak.api.PropertyState;
-import org.apache.jackrabbit.oak.api.Type;
-import org.apache.jackrabbit.oak.namepath.NamePathMapper;
-
-/**
- * Implementation of {@link Value} based on {@code PropertyState}.
- */
-public class ValueImpl implements JackrabbitValue {
-
-    public static Blob getBlob(Value value) {
-        checkState(value instanceof ValueImpl);
-        return ((ValueImpl) value).getBlob();
-    }
-
-    private final PropertyState propertyState;
-    private final int index;
-    private final NamePathMapper namePathMapper;
-
-    private InputStream stream = null;
-
-    /**
-     * Create a new {@code Value} instance
-     * @param property  The property state this instance is based on
-     * @param index  The index
-     * @param namePathMapper The name/path mapping used for converting JCR names/paths to
-     * the internal representation.
-     * @throws IllegalArgumentException if {@code index < propertyState.count()}
-     */
-    ValueImpl(PropertyState property, int index, NamePathMapper namePathMapper) {
-        checkArgument(index < property.count());
-        this.propertyState = property;
-        this.index = index;
-        this.namePathMapper = namePathMapper;
-    }
-
-    /**
-     * Create a new {@code Value} instance
-     * @param property  The property state this instance is based on
-     * @param namePathMapper The name/path mapping used for converting JCR names/paths to
-     * the internal representation.
-     * @throws IllegalArgumentException if {@code property.isArray()} is {@code true}.
-     */
-    ValueImpl(PropertyState property, NamePathMapper namePathMapper) {
-        this(checkSingleValued(property), 0, namePathMapper);
-    }
-
-    Blob getBlob() {
-        return propertyState.getValue(Type.BINARY, index);
-    }
-
-    /**
-     * Same as {@link #getString()} unless that names and paths are returned in their
-     * Oak representation instead of being mapped to their JCR representation.
-     * @return  A String representation of the value of this property.
-     */
-    public String getOakString() {
-        return propertyState.getValue(Type.STRING, index);
-    }
-
-    private static PropertyState checkSingleValued(PropertyState property) {
-        checkArgument(!property.isArray());
-        return property;
-    }
-
-    //--------------------------------------------------------------< Value >---
-
-    /**
-     * @see javax.jcr.Value#getType()
-     */
-    @Override
-    public int getType() {
-        return propertyState.getType().tag();
-    }
-
-    /**
-     * @see javax.jcr.Value#getBoolean()
-     */
-    @Override
-    public boolean getBoolean() throws RepositoryException {
-        switch (getType()) {
-            case PropertyType.STRING:
-            case PropertyType.BINARY:
-            case PropertyType.BOOLEAN:
-                return propertyState.getValue(Type.BOOLEAN, index);
-            default:
-                throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getDate()
-     */
-    @Override
-    public Calendar getDate() throws RepositoryException {
-        try {
-            switch (getType()) {
-                case PropertyType.STRING:
-                case PropertyType.BINARY:
-                case PropertyType.DATE:
-                    String value = propertyState.getValue(Type.DATE, index);
-                    return Conversions.convert(value).toCalendar();
-                case PropertyType.LONG:
-                case PropertyType.DOUBLE:
-                case PropertyType.DECIMAL:
-                    return Conversions.convert(propertyState.getValue(Type.LONG, index)).toCalendar();
-                default:
-                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
-            }
-        }
-        catch (IllegalArgumentException e) {
-            throw new ValueFormatException("Error converting value to date", e);
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getDecimal()
-     */
-    @Override
-    public BigDecimal getDecimal() throws RepositoryException {
-        try {
-            switch (getType()) {
-                case PropertyType.STRING:
-                case PropertyType.BINARY:
-                case PropertyType.LONG:
-                case PropertyType.DOUBLE:
-                case PropertyType.DATE:
-                case PropertyType.DECIMAL:
-                    return propertyState.getValue(Type.DECIMAL, index);
-                default:
-                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
-            }
-        }
-        catch (IllegalArgumentException e) {
-            throw new ValueFormatException("Error converting value to decimal", e);
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getDouble()
-     */
-    @Override
-    public double getDouble() throws RepositoryException {
-        try {
-            switch (getType()) {
-                case PropertyType.STRING:
-                case PropertyType.BINARY:
-                case PropertyType.LONG:
-                case PropertyType.DOUBLE:
-                case PropertyType.DATE:
-                case PropertyType.DECIMAL:
-                    return propertyState.getValue(Type.DOUBLE, index);
-                default:
-                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
-            }
-        }
-        catch (IllegalArgumentException e) {
-            throw new ValueFormatException("Error converting value to double", e);
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getLong()
-     */
-    @Override
-    public long getLong() throws RepositoryException {
-        try {
-            switch (getType()) {
-                case PropertyType.STRING:
-                case PropertyType.BINARY:
-                case PropertyType.LONG:
-                case PropertyType.DOUBLE:
-                case PropertyType.DATE:
-                case PropertyType.DECIMAL:
-                    return propertyState.getValue(Type.LONG, index);
-                default:
-                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
-            }
-        }
-        catch (IllegalArgumentException e) {
-            throw new ValueFormatException("Error converting value to long", e);
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getString()
-     */
-    @Override
-    public String getString() throws RepositoryException {
-        checkState(getType() != PropertyType.BINARY || stream == null,
-                "getStream has previously been called on this Value instance. " +
-                "In this case a new Value instance must be acquired in order to successfully call this method.");
-
-        switch (getType()) {
-            case PropertyType.NAME:
-                return namePathMapper.getJcrName(getOakString());
-            case PropertyType.PATH:
-                String s = getOakString();
-                if (s.startsWith("[") && s.endsWith("]")) {
-                    // identifier paths are returned as-is (JCR 2.0, 3.4.3.1)
-                    return s;
-                } else {
-                    return namePathMapper.getJcrPath(s);
-                }
-            default:
-                return getOakString();
-        }
-    }
-
-    /**
-     * @see javax.jcr.Value#getStream()
-     */
-    @Override
-    public InputStream getStream() throws IllegalStateException {
-        if (stream == null) {
-            stream = getBlob().getNewStream();
-        }
-        return stream;
-    }
-
-    /**
-     * @see javax.jcr.Value#getBinary()
-     */
-    @Override
-    public Binary getBinary() throws RepositoryException {
-        return new BinaryImpl(this);
-    }
-
-    @Override
-    public String getContentIdentity() {
-        return getBlob().getContentIdentity();
-    }
-
-    //-------------------------------------------------------------< Object >---
-
-    /**
-     * @see Object#equals(Object)
-     */
-    @Override
-    public boolean equals(Object other) {
-        if (other instanceof ValueImpl) {
-            ValueImpl that = (ValueImpl) other;
-            Type<?> type = propertyState.getType();
-            if (type.isArray()) {
-                type = type.getBaseType();
-            }
-            return type.tag() == that.propertyState.getType().tag()
-                    && Objects.equal(
-                            propertyState.getValue(type, index),
-                            that.propertyState.getValue(type, that.index));
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * @see Object#hashCode()
-     */
-    @Override
-    public int hashCode() {
-        if (getType() == PropertyType.BINARY) {
-            return propertyState.getValue(Type.BINARY, index).hashCode();
-        } else {
-            return getOakString().hashCode();
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getOakString();
-    }
-
+/*
+ * 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.plugins.value;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.Binary;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import com.google.common.base.Objects;
+import org.apache.jackrabbit.api.JackrabbitValue;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+
+/**
+ * Implementation of {@link Value} based on {@code PropertyState}.
+ */
+public class ValueImpl implements JackrabbitValue {
+
+    public static Blob getBlob(Value value) {
+        checkState(value instanceof ValueImpl);
+        return ((ValueImpl) value).getBlob();
+    }
+
+    private final PropertyState propertyState;
+    private final int index;
+    private final NamePathMapper namePathMapper;
+
+    private InputStream stream = null;
+
+    /**
+     * Create a new {@code Value} instance
+     * @param property  The property state this instance is based on
+     * @param index  The index
+     * @param namePathMapper The name/path mapping used for converting JCR names/paths to
+     * the internal representation.
+     * @throws IllegalArgumentException if {@code index < propertyState.count()}
+     */
+    ValueImpl(PropertyState property, int index, NamePathMapper namePathMapper) {
+        checkArgument(index < property.count());
+        this.propertyState = property;
+        this.index = index;
+        this.namePathMapper = namePathMapper;
+    }
+
+    /**
+     * Create a new {@code Value} instance
+     * @param property  The property state this instance is based on
+     * @param namePathMapper The name/path mapping used for converting JCR names/paths to
+     * the internal representation.
+     * @throws IllegalArgumentException if {@code property.isArray()} is {@code true}.
+     */
+    ValueImpl(PropertyState property, NamePathMapper namePathMapper) {
+        this(checkSingleValued(property), 0, namePathMapper);
+    }
+
+    Blob getBlob() {
+        return propertyState.getValue(Type.BINARY, index);
+    }
+
+    /**
+     * Same as {@link #getString()} unless that names and paths are returned in their
+     * Oak representation instead of being mapped to their JCR representation.
+     * @return  A String representation of the value of this property.
+     */
+    public String getOakString() {
+        return propertyState.getValue(Type.STRING, index);
+    }
+
+    private static PropertyState checkSingleValued(PropertyState property) {
+        checkArgument(!property.isArray());
+        return property;
+    }
+
+    //--------------------------------------------------------------< Value >---
+
+    /**
+     * @see javax.jcr.Value#getType()
+     */
+    @Override
+    public int getType() {
+        return propertyState.getType().tag();
+    }
+
+    /**
+     * @see javax.jcr.Value#getBoolean()
+     */
+    @Override
+    public boolean getBoolean() throws RepositoryException {
+        switch (getType()) {
+            case PropertyType.STRING:
+            case PropertyType.BINARY:
+            case PropertyType.BOOLEAN:
+                return propertyState.getValue(Type.BOOLEAN, index);
+            default:
+                throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getDate()
+     */
+    @Override
+    public Calendar getDate() throws RepositoryException {
+        try {
+            switch (getType()) {
+                case PropertyType.STRING:
+                case PropertyType.BINARY:
+                case PropertyType.DATE:
+                    String value = propertyState.getValue(Type.DATE, index);
+                    return Conversions.convert(value).toCalendar();
+                case PropertyType.LONG:
+                case PropertyType.DOUBLE:
+                case PropertyType.DECIMAL:
+                    return Conversions.convert(propertyState.getValue(Type.LONG, index)).toCalendar();
+                default:
+                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw new ValueFormatException("Error converting value to date", e);
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getDecimal()
+     */
+    @Override
+    public BigDecimal getDecimal() throws RepositoryException {
+        try {
+            switch (getType()) {
+                case PropertyType.STRING:
+                case PropertyType.BINARY:
+                case PropertyType.LONG:
+                case PropertyType.DOUBLE:
+                case PropertyType.DATE:
+                case PropertyType.DECIMAL:
+                    return propertyState.getValue(Type.DECIMAL, index);
+                default:
+                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw new ValueFormatException("Error converting value to decimal", e);
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getDouble()
+     */
+    @Override
+    public double getDouble() throws RepositoryException {
+        try {
+            switch (getType()) {
+                case PropertyType.STRING:
+                case PropertyType.BINARY:
+                case PropertyType.LONG:
+                case PropertyType.DOUBLE:
+                case PropertyType.DATE:
+                case PropertyType.DECIMAL:
+                    return propertyState.getValue(Type.DOUBLE, index);
+                default:
+                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw new ValueFormatException("Error converting value to double", e);
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getLong()
+     */
+    @Override
+    public long getLong() throws RepositoryException {
+        try {
+            switch (getType()) {
+                case PropertyType.STRING:
+                case PropertyType.BINARY:
+                case PropertyType.LONG:
+                case PropertyType.DOUBLE:
+                case PropertyType.DATE:
+                case PropertyType.DECIMAL:
+                    return propertyState.getValue(Type.LONG, index);
+                default:
+                    throw new ValueFormatException("Incompatible type " + PropertyType.nameFromValue(getType()));
+            }
+        }
+        catch (IllegalArgumentException e) {
+            throw new ValueFormatException("Error converting value to long", e);
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getString()
+     */
+    @Override
+    public String getString() throws RepositoryException {
+        checkState(getType() != PropertyType.BINARY || stream == null,
+                "getStream has previously been called on this Value instance. " +
+                "In this case a new Value instance must be acquired in order to successfully call this method.");
+
+        switch (getType()) {
+            case PropertyType.NAME:
+                return namePathMapper.getJcrName(getOakString());
+            case PropertyType.PATH:
+                String s = getOakString();
+                if (s.startsWith("[") && s.endsWith("]")) {
+                    // identifier paths are returned as-is (JCR 2.0, 3.4.3.1)
+                    return s;
+                } else {
+                    return namePathMapper.getJcrPath(s);
+                }
+            default:
+                return getOakString();
+        }
+    }
+
+    /**
+     * @see javax.jcr.Value#getStream()
+     */
+    @Override
+    public InputStream getStream() throws IllegalStateException {
+        if (stream == null) {
+            stream = getBlob().getNewStream();
+        }
+        return stream;
+    }
+
+    /**
+     * @see javax.jcr.Value#getBinary()
+     */
+    @Override
+    public Binary getBinary() throws RepositoryException {
+        return new BinaryImpl(this);
+    }
+
+    @Override
+    public String getContentIdentity() {
+        return getBlob().getContentIdentity();
+    }
+
+    //-------------------------------------------------------------< Object >---
+
+    /**
+     * @see Object#equals(Object)
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof ValueImpl) {
+            ValueImpl that = (ValueImpl) other;
+            Type<?> type = propertyState.getType();
+            if (type.isArray()) {
+                type = type.getBaseType();
+            }
+            return type.tag() == that.propertyState.getType().tag()
+                    && Objects.equal(
+                            propertyState.getValue(type, index),
+                            that.propertyState.getValue(type, that.index));
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @see Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        if (getType() == PropertyType.BINARY) {
+            return propertyState.getValue(Type.BINARY, index).hashCode();
+        } else {
+            return getOakString().hashCode();
+        }
+    }
+
+    @Override
+    public String toString() {
+        return getOakString();
+    }
+
 }
\ No newline at end of file

Modified: jackrabbit/oak/branches/1.0/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.0/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java?rev=1595852&r1=1595851&r2=1595852&view=diff
==============================================================================
--- jackrabbit/oak/branches/1.0/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java (original)
+++ jackrabbit/oak/branches/1.0/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/repository/RepositoryImpl.java Mon May 19 11:57:26 2014
@@ -1,444 +1,444 @@
-/*
- * 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.repository;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.Collections.emptyMap;
-import static java.util.Collections.singletonMap;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.jcr.Credentials;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-import javax.jcr.Value;
-import javax.security.auth.login.LoginException;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.util.concurrent.FutureCallback;
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableScheduledFuture;
-import com.google.common.util.concurrent.ListeningScheduledExecutorService;
-import com.google.common.util.concurrent.MoreExecutors;
-import org.apache.jackrabbit.api.JackrabbitRepository;
-import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
-import org.apache.jackrabbit.commons.SimpleValueFactory;
-import org.apache.jackrabbit.oak.api.ContentRepository;
-import org.apache.jackrabbit.oak.api.ContentSession;
-import org.apache.jackrabbit.oak.api.jmx.SessionMBean;
-import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
-import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy;
-import org.apache.jackrabbit.oak.jcr.session.SessionContext;
-import org.apache.jackrabbit.oak.jcr.session.SessionStats;
-import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
-import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
-import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
-import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
-import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
-import org.apache.jackrabbit.oak.stats.Clock;
-import org.apache.jackrabbit.oak.stats.StatisticManager;
-import org.apache.jackrabbit.oak.util.GenericDescriptors;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * TODO document
- */
-public class RepositoryImpl implements JackrabbitRepository {
-
-    /**
-     * logger instance
-     */
-    private static final Logger log = LoggerFactory.getLogger(RepositoryImpl.class);
-
-    /**
-     * Name of the session attribute value determining the session refresh
-     * interval in seconds.
-     *
-     * @see org.apache.jackrabbit.oak.jcr.session.RefreshStrategy
-     */
-    public static final String REFRESH_INTERVAL = "oak.refresh-interval";
-
-    /**
-     * Name of the session attribute for enabling relaxed locking rules
-     *
-     * @see <a href="https://issues.apache.org/jira/browse/OAK-1329">OAK-1329</a>
-     */
-    public static final String RELAXED_LOCKING = "oak.relaxed-locking";
-
-    private final GenericDescriptors descriptors;
-    private final ContentRepository contentRepository;
-    protected final Whiteboard whiteboard;
-    private final SecurityProvider securityProvider;
-    private final int observationQueueLength;
-    private final CommitRateLimiter commitRateLimiter;
-
-    private final Clock clock;
-
-    /**
-     * {@link ThreadLocal} counter that keeps track of the save operations
-     * performed per thread so far. This is is then used to determine if
-     * the current session needs to be refreshed to see the changes done by
-     * another session in the same thread.
-     * <p>
-     * <b>Note</b> - This thread local is never cleared. However, we only
-     * store a {@link Long} instance and do not derive from
-     * {@link ThreadLocal} so that (class loader) leaks typically associated
-     * with thread locals do not occur.
-     */
-    private final ThreadLocal<Long> threadSaveCount = new ThreadLocal<Long>();
-
-    private final ListeningScheduledExecutorService scheduledExecutor =
-            MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
-    private final StatisticManager statisticManager;
-
-    public RepositoryImpl(@Nonnull ContentRepository contentRepository,
-                          @Nonnull Whiteboard whiteboard,
-                          @Nonnull SecurityProvider securityProvider,
-                          int observationQueueLength,
-                          CommitRateLimiter commitRateLimiter) {
-        this.contentRepository = checkNotNull(contentRepository);
-        this.whiteboard = checkNotNull(whiteboard);
-        this.securityProvider = checkNotNull(securityProvider);
-        this.observationQueueLength = observationQueueLength;
-        this.commitRateLimiter = commitRateLimiter;
-        this.descriptors = determineDescriptors();
-        this.statisticManager = new StatisticManager(whiteboard, scheduledExecutor);
-        this.clock = new Clock.Fast(scheduledExecutor);
-    }
-
-    //---------------------------------------------------------< Repository >---
-    /**
-     * @see javax.jcr.Repository#getDescriptorKeys()
-     */
-    @Override
-    public String[] getDescriptorKeys() {
-        return descriptors.getKeys();
-    }
-
-    /**
-     * @see Repository#isStandardDescriptor(String)
-     */
-    @Override
-    public boolean isStandardDescriptor(String key) {
-        return descriptors.isStandardDescriptor(key);
-    }
-
-    /**
-     * @see javax.jcr.Repository#getDescriptor(String)
-     */
-    @Override
-    public String getDescriptor(String key) {
-        try {
-            Value v = getDescriptorValue(key);
-            return v == null
-                    ? null
-                    : v.getString();
-        } catch (RepositoryException e) {
-            log.debug("Error converting value for descriptor with key {} to string", key);
-            return null;
-        }
-    }
-
-    /**
-     * @see javax.jcr.Repository#getDescriptorValue(String)
-     */
-    @Override
-    public Value getDescriptorValue(String key) {
-        return descriptors.getValue(key);
-    }
-
-    /**
-     * @see javax.jcr.Repository#getDescriptorValues(String)
-     */
-    @Override
-    public Value[] getDescriptorValues(String key) {
-        return descriptors.getValues(key);
-    }
-
-    /**
-     * @see javax.jcr.Repository#isSingleValueDescriptor(String)
-     */
-    @Override
-    public boolean isSingleValueDescriptor(String key) {
-        return descriptors.isSingleValueDescriptor(key);
-    }
-
-    /**
-     * @see javax.jcr.Repository#login(javax.jcr.Credentials, String)
-     */
-    @Override
-    public Session login(@Nullable Credentials credentials, @Nullable String workspaceName)
-            throws RepositoryException {
-        return login(credentials, workspaceName, null);
-    }
-
-    /**
-     * Calls {@link Repository#login(Credentials, String)} with
-     * {@code null} arguments.
-     *
-     * @return logged in session
-     * @throws RepositoryException if an error occurs
-     */
-    @Override
-    public Session login() throws RepositoryException {
-        return login(null, null, null);
-    }
-
-    /**
-     * Calls {@link Repository#login(Credentials, String)} with
-     * the given credentials and a {@code null} workspace name.
-     *
-     * @param credentials login credentials
-     * @return logged in session
-     * @throws RepositoryException if an error occurs
-     */
-    @Override
-    public Session login(Credentials credentials) throws RepositoryException {
-        return login(credentials, null, null);
-    }
-
-    /**
-     * Calls {@link Repository#login(Credentials, String)} with
-     * {@code null} credentials and the given workspace name.
-     *
-     * @param workspace workspace name
-     * @return logged in session
-     * @throws RepositoryException if an error occurs
-     */
-    @Override
-    public Session login(String workspace) throws RepositoryException {
-        return login(null, workspace, null);
-    }
-
-    //------------------------------------------------------------< JackrabbitRepository >---
-
-    @Override
-    public Session login(@CheckForNull Credentials credentials, @CheckForNull String workspaceName,
-            @CheckForNull Map<String, Object> attributes) throws RepositoryException {
-        try {
-            if (attributes == null) {
-                attributes = Collections.emptyMap();
-            }
-            Long refreshInterval = getRefreshInterval(credentials);
-            if (refreshInterval == null) {
-                refreshInterval = getRefreshInterval(attributes);
-            } else if (attributes.containsKey(REFRESH_INTERVAL)) {
-                throw new RepositoryException("Duplicate attribute '" + REFRESH_INTERVAL + "'.");
-            }
-            boolean relaxedLocking = getRelaxedLocking(attributes);
-
-            RefreshStrategy refreshStrategy = createRefreshStrategy(refreshInterval);
-            ContentSession contentSession = contentRepository.login(credentials, workspaceName);
-            SessionDelegate sessionDelegate = createSessionDelegate(refreshStrategy, contentSession);
-            SessionContext context = createSessionContext(
-                    statisticManager, securityProvider,
-                    createAttributes(refreshInterval, relaxedLocking),
-                    sessionDelegate, observationQueueLength, commitRateLimiter);
-            return context.getSession();
-        } catch (LoginException e) {
-            throw new javax.jcr.LoginException(e.getMessage(), e);
-        }
-    }
-
-    private SessionDelegate createSessionDelegate(
-            final RefreshStrategy refreshStrategy,
-            final ContentSession contentSession) {
-        return new SessionDelegate(
-                contentSession, securityProvider, refreshStrategy,
-                threadSaveCount, statisticManager, clock) {
-            // Defer session MBean registration to avoid cluttering the
-            // JMX name space with short lived sessions
-            ListenableScheduledFuture<Registration> registration = scheduledExecutor.schedule(
-                    new RegistrationCallable(getSessionStats(), whiteboard), 1, TimeUnit.MINUTES);
-
-            @Override
-            public void logout() {
-                // Cancel session MBean registration and unregister MBean
-                // if registration succeed before the cancellation
-                registration.cancel(false);
-                Futures.addCallback(registration, new FutureCallback<Registration>() {
-                    @Override
-                    public void onSuccess(Registration registration) {
-                        registration.unregister();
-                    }
-
-                    @Override
-                    public void onFailure(Throwable t) {
-                    }
-                });
-
-                super.logout();
-            }
-        };
-    }
-
-    @Override
-    public void shutdown() {
-        statisticManager.dispose();
-        scheduledExecutor.shutdown();
-    }
-
-    //------------------------------------------------------------< internal >---
-
-    /**
-     * Factory method for creating a {@link SessionContext} instance for
-     * a new session. Called by {@link #login()}. Can be overridden by
-     * subclasses to customize the session implementation.
-     *
-     * @return session context
-     */
-    protected SessionContext createSessionContext(
-            StatisticManager statisticManager, SecurityProvider securityProvider,
-            Map<String, Object> attributes, SessionDelegate delegate, int observationQueueLength,
-            CommitRateLimiter commitRateLimiter) {
-        return new SessionContext(this, statisticManager, securityProvider, whiteboard, attributes,
-                delegate, observationQueueLength, commitRateLimiter);
-    }
-
-    /**
-     * Provides descriptors for current repository implementations. Can be overridden
-     * by the subclasses to add more values to the descriptor
-     * @return  repository descriptor
-     */
-    protected GenericDescriptors determineDescriptors() {
-        return new JcrDescriptorsImpl(contentRepository.getDescriptors(), new SimpleValueFactory());
-    }
-
-    /**
-     * Returns the descriptors associated with the repository
-     * @return repository descriptor
-     */
-    protected GenericDescriptors getDescriptors() {
-        return descriptors;
-    }
-
-//------------------------------------------------------------< private >---
-
-    private static Long getRefreshInterval(Credentials credentials) {
-        if (credentials instanceof SimpleCredentials) {
-            Object value = ((SimpleCredentials) credentials).getAttribute(REFRESH_INTERVAL);
-            return toLong(value);
-        } else if (credentials instanceof TokenCredentials) {
-            String value = ((TokenCredentials) credentials).getAttribute(REFRESH_INTERVAL);
-            if (value != null) {
-                return toLong(value);
-            }
-        }
-        return null;
-    }
-
-    private static Long getRefreshInterval(Map<String, Object> attributes) {
-        return toLong(attributes.get(REFRESH_INTERVAL));
-    }
-
-    private static boolean getRelaxedLocking(Map<String, Object> attributes) {
-        Object value = attributes.get(RELAXED_LOCKING);
-        if (value instanceof Boolean) {
-            return (Boolean) value;
-        } else if (value instanceof String) {
-            return Boolean.parseBoolean((String) value);
-        } else {
-            return false;
-        }
-    }
-
-    private static Long toLong(Object value) {
-        if (value instanceof Long) {
-            return (Long) value;
-        } else if (value instanceof Integer) {
-            return ((Integer) value).longValue();
-        } else if (value instanceof String) {
-            return toLong((String) value);
-        } else {
-            return null;
-        }
-    }
-
-    private static Long toLong(String longValue) {
-        try {
-            return Long.valueOf(longValue);
-        } catch (NumberFormatException e) {
-            log.warn("Invalid value '" + longValue + "' for " + REFRESH_INTERVAL +
-                    ". Expected long. ", e);
-            return null;
-        }
-    }
-
-    private static Map<String, Object> createAttributes(
-            Long refreshInterval, boolean relaxedLocking) {
-        if (refreshInterval == null && !relaxedLocking) {
-            return emptyMap();
-        } else if (refreshInterval == null) {
-            return singletonMap(RELAXED_LOCKING, (Object) Boolean.valueOf(relaxedLocking));
-        } else if (!relaxedLocking) {
-            return singletonMap(REFRESH_INTERVAL, (Object) refreshInterval);
-        } else {
-            return ImmutableMap.of(
-                    REFRESH_INTERVAL, (Object) refreshInterval,
-                    RELAXED_LOCKING,  (Object) Boolean.valueOf(relaxedLocking));
-        }
-    }
-
-    /**
-     * Auto refresh logic for sessions, which is done to enhance backwards compatibility with
-     * Jackrabbit 2.
-     * <p>
-     * A sessions is automatically refreshed when
-     * <ul>
-     *     <li>it has not been accessed for the number of seconds specified by the
-     *         {@code refreshInterval} parameter,</li>
-     *     <li>an observation event has been delivered to a listener registered from within this
-     *         session,</li>
-     *     <li>an updated occurred through a different session from <em>within the same
-     *         thread.</em></li>
-     * </ul>
-     * In addition a warning is logged once per session if the session is accessed after one
-     * minute of inactivity.
-     */
-    private RefreshStrategy createRefreshStrategy(Long refreshInterval) {
-        if (refreshInterval == null) {
-            return new RefreshStrategy.LogOnce(60);
-        } else {
-            return new RefreshStrategy.Timed(refreshInterval);
-        }
-    }
-
-    private static class RegistrationCallable implements Callable<Registration> {
-        private final SessionStats sessionStats;
-        private final Whiteboard whiteboard;
-
-        public RegistrationCallable(SessionStats sessionStats, Whiteboard whiteboard) {
-            this.sessionStats = sessionStats;
-            this.whiteboard = whiteboard;
-        }
-
-        @Override
-        public Registration call() throws Exception {
-            return WhiteboardUtils.registerMBean(whiteboard, SessionMBean.class,
-                    sessionStats, SessionMBean.TYPE, sessionStats.toString());
-        }
-    }
-}
+/*
+ * 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.repository;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.emptyMap;
+import static java.util.Collections.singletonMap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+import javax.security.auth.login.LoginException;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableScheduledFuture;
+import com.google.common.util.concurrent.ListeningScheduledExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.security.authentication.token.TokenCredentials;
+import org.apache.jackrabbit.commons.SimpleValueFactory;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.jmx.SessionMBean;
+import org.apache.jackrabbit.oak.jcr.delegate.SessionDelegate;
+import org.apache.jackrabbit.oak.jcr.session.RefreshStrategy;
+import org.apache.jackrabbit.oak.jcr.session.SessionContext;
+import org.apache.jackrabbit.oak.jcr.session.SessionStats;
+import org.apache.jackrabbit.oak.plugins.observation.CommitRateLimiter;
+import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.jackrabbit.oak.stats.Clock;
+import org.apache.jackrabbit.oak.stats.StatisticManager;
+import org.apache.jackrabbit.oak.util.GenericDescriptors;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TODO document
+ */
+public class RepositoryImpl implements JackrabbitRepository {
+
+    /**
+     * logger instance
+     */
+    private static final Logger log = LoggerFactory.getLogger(RepositoryImpl.class);
+
+    /**
+     * Name of the session attribute value determining the session refresh
+     * interval in seconds.
+     *
+     * @see org.apache.jackrabbit.oak.jcr.session.RefreshStrategy
+     */
+    public static final String REFRESH_INTERVAL = "oak.refresh-interval";
+
+    /**
+     * Name of the session attribute for enabling relaxed locking rules
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/OAK-1329">OAK-1329</a>
+     */
+    public static final String RELAXED_LOCKING = "oak.relaxed-locking";
+
+    private final GenericDescriptors descriptors;
+    private final ContentRepository contentRepository;
+    protected final Whiteboard whiteboard;
+    private final SecurityProvider securityProvider;
+    private final int observationQueueLength;
+    private final CommitRateLimiter commitRateLimiter;
+
+    private final Clock clock;
+
+    /**
+     * {@link ThreadLocal} counter that keeps track of the save operations
+     * performed per thread so far. This is is then used to determine if
+     * the current session needs to be refreshed to see the changes done by
+     * another session in the same thread.
+     * <p>
+     * <b>Note</b> - This thread local is never cleared. However, we only
+     * store a {@link Long} instance and do not derive from
+     * {@link ThreadLocal} so that (class loader) leaks typically associated
+     * with thread locals do not occur.
+     */
+    private final ThreadLocal<Long> threadSaveCount = new ThreadLocal<Long>();
+
+    private final ListeningScheduledExecutorService scheduledExecutor =
+            MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
+    private final StatisticManager statisticManager;
+
+    public RepositoryImpl(@Nonnull ContentRepository contentRepository,
+                          @Nonnull Whiteboard whiteboard,
+                          @Nonnull SecurityProvider securityProvider,
+                          int observationQueueLength,
+                          CommitRateLimiter commitRateLimiter) {
+        this.contentRepository = checkNotNull(contentRepository);
+        this.whiteboard = checkNotNull(whiteboard);
+        this.securityProvider = checkNotNull(securityProvider);
+        this.observationQueueLength = observationQueueLength;
+        this.commitRateLimiter = commitRateLimiter;
+        this.descriptors = determineDescriptors();
+        this.statisticManager = new StatisticManager(whiteboard, scheduledExecutor);
+        this.clock = new Clock.Fast(scheduledExecutor);
+    }
+
+    //---------------------------------------------------------< Repository >---
+    /**
+     * @see javax.jcr.Repository#getDescriptorKeys()
+     */
+    @Override
+    public String[] getDescriptorKeys() {
+        return descriptors.getKeys();
+    }
+
+    /**
+     * @see Repository#isStandardDescriptor(String)
+     */
+    @Override
+    public boolean isStandardDescriptor(String key) {
+        return descriptors.isStandardDescriptor(key);
+    }
+
+    /**
+     * @see javax.jcr.Repository#getDescriptor(String)
+     */
+    @Override
+    public String getDescriptor(String key) {
+        try {
+            Value v = getDescriptorValue(key);
+            return v == null
+                    ? null
+                    : v.getString();
+        } catch (RepositoryException e) {
+            log.debug("Error converting value for descriptor with key {} to string", key);
+            return null;
+        }
+    }
+
+    /**
+     * @see javax.jcr.Repository#getDescriptorValue(String)
+     */
+    @Override
+    public Value getDescriptorValue(String key) {
+        return descriptors.getValue(key);
+    }
+
+    /**
+     * @see javax.jcr.Repository#getDescriptorValues(String)
+     */
+    @Override
+    public Value[] getDescriptorValues(String key) {
+        return descriptors.getValues(key);
+    }
+
+    /**
+     * @see javax.jcr.Repository#isSingleValueDescriptor(String)
+     */
+    @Override
+    public boolean isSingleValueDescriptor(String key) {
+        return descriptors.isSingleValueDescriptor(key);
+    }
+
+    /**
+     * @see javax.jcr.Repository#login(javax.jcr.Credentials, String)
+     */
+    @Override
+    public Session login(@Nullable Credentials credentials, @Nullable String workspaceName)
+            throws RepositoryException {
+        return login(credentials, workspaceName, null);
+    }
+
+    /**
+     * Calls {@link Repository#login(Credentials, String)} with
+     * {@code null} arguments.
+     *
+     * @return logged in session
+     * @throws RepositoryException if an error occurs
+     */
+    @Override
+    public Session login() throws RepositoryException {
+        return login(null, null, null);
+    }
+
+    /**
+     * Calls {@link Repository#login(Credentials, String)} with
+     * the given credentials and a {@code null} workspace name.
+     *
+     * @param credentials login credentials
+     * @return logged in session
+     * @throws RepositoryException if an error occurs
+     */
+    @Override
+    public Session login(Credentials credentials) throws RepositoryException {
+        return login(credentials, null, null);
+    }
+
+    /**
+     * Calls {@link Repository#login(Credentials, String)} with
+     * {@code null} credentials and the given workspace name.
+     *
+     * @param workspace workspace name
+     * @return logged in session
+     * @throws RepositoryException if an error occurs
+     */
+    @Override
+    public Session login(String workspace) throws RepositoryException {
+        return login(null, workspace, null);
+    }
+
+    //------------------------------------------------------------< JackrabbitRepository >---
+
+    @Override
+    public Session login(@CheckForNull Credentials credentials, @CheckForNull String workspaceName,
+            @CheckForNull Map<String, Object> attributes) throws RepositoryException {
+        try {
+            if (attributes == null) {
+                attributes = Collections.emptyMap();
+            }
+            Long refreshInterval = getRefreshInterval(credentials);
+            if (refreshInterval == null) {
+                refreshInterval = getRefreshInterval(attributes);
+            } else if (attributes.containsKey(REFRESH_INTERVAL)) {
+                throw new RepositoryException("Duplicate attribute '" + REFRESH_INTERVAL + "'.");
+            }
+            boolean relaxedLocking = getRelaxedLocking(attributes);
+
+            RefreshStrategy refreshStrategy = createRefreshStrategy(refreshInterval);
+            ContentSession contentSession = contentRepository.login(credentials, workspaceName);
+            SessionDelegate sessionDelegate = createSessionDelegate(refreshStrategy, contentSession);
+            SessionContext context = createSessionContext(
+                    statisticManager, securityProvider,
+                    createAttributes(refreshInterval, relaxedLocking),
+                    sessionDelegate, observationQueueLength, commitRateLimiter);
+            return context.getSession();
+        } catch (LoginException e) {
+            throw new javax.jcr.LoginException(e.getMessage(), e);
+        }
+    }
+
+    private SessionDelegate createSessionDelegate(
+            final RefreshStrategy refreshStrategy,
+            final ContentSession contentSession) {
+        return new SessionDelegate(
+                contentSession, securityProvider, refreshStrategy,
+                threadSaveCount, statisticManager, clock) {
+            // Defer session MBean registration to avoid cluttering the
+            // JMX name space with short lived sessions
+            ListenableScheduledFuture<Registration> registration = scheduledExecutor.schedule(
+                    new RegistrationCallable(getSessionStats(), whiteboard), 1, TimeUnit.MINUTES);
+
+            @Override
+            public void logout() {
+                // Cancel session MBean registration and unregister MBean
+                // if registration succeed before the cancellation
+                registration.cancel(false);
+                Futures.addCallback(registration, new FutureCallback<Registration>() {
+                    @Override
+                    public void onSuccess(Registration registration) {
+                        registration.unregister();
+                    }
+
+                    @Override
+                    public void onFailure(Throwable t) {
+                    }
+                });
+
+                super.logout();
+            }
+        };
+    }
+
+    @Override
+    public void shutdown() {
+        statisticManager.dispose();
+        scheduledExecutor.shutdown();
+    }
+
+    //------------------------------------------------------------< internal >---
+
+    /**
+     * Factory method for creating a {@link SessionContext} instance for
+     * a new session. Called by {@link #login()}. Can be overridden by
+     * subclasses to customize the session implementation.
+     *
+     * @return session context
+     */
+    protected SessionContext createSessionContext(
+            StatisticManager statisticManager, SecurityProvider securityProvider,
+            Map<String, Object> attributes, SessionDelegate delegate, int observationQueueLength,
+            CommitRateLimiter commitRateLimiter) {
+        return new SessionContext(this, statisticManager, securityProvider, whiteboard, attributes,
+                delegate, observationQueueLength, commitRateLimiter);
+    }
+
+    /**
+     * Provides descriptors for current repository implementations. Can be overridden
+     * by the subclasses to add more values to the descriptor
+     * @return  repository descriptor
+     */
+    protected GenericDescriptors determineDescriptors() {
+        return new JcrDescriptorsImpl(contentRepository.getDescriptors(), new SimpleValueFactory());
+    }
+
+    /**
+     * Returns the descriptors associated with the repository
+     * @return repository descriptor
+     */
+    protected GenericDescriptors getDescriptors() {
+        return descriptors;
+    }
+
+//------------------------------------------------------------< private >---
+
+    private static Long getRefreshInterval(Credentials credentials) {
+        if (credentials instanceof SimpleCredentials) {
+            Object value = ((SimpleCredentials) credentials).getAttribute(REFRESH_INTERVAL);
+            return toLong(value);
+        } else if (credentials instanceof TokenCredentials) {
+            String value = ((TokenCredentials) credentials).getAttribute(REFRESH_INTERVAL);
+            if (value != null) {
+                return toLong(value);
+            }
+        }
+        return null;
+    }
+
+    private static Long getRefreshInterval(Map<String, Object> attributes) {
+        return toLong(attributes.get(REFRESH_INTERVAL));
+    }
+
+    private static boolean getRelaxedLocking(Map<String, Object> attributes) {
+        Object value = attributes.get(RELAXED_LOCKING);
+        if (value instanceof Boolean) {
+            return (Boolean) value;
+        } else if (value instanceof String) {
+            return Boolean.parseBoolean((String) value);
+        } else {
+            return false;
+        }
+    }
+
+    private static Long toLong(Object value) {
+        if (value instanceof Long) {
+            return (Long) value;
+        } else if (value instanceof Integer) {
+            return ((Integer) value).longValue();
+        } else if (value instanceof String) {
+            return toLong((String) value);
+        } else {
+            return null;
+        }
+    }
+
+    private static Long toLong(String longValue) {
+        try {
+            return Long.valueOf(longValue);
+        } catch (NumberFormatException e) {
+            log.warn("Invalid value '" + longValue + "' for " + REFRESH_INTERVAL +
+                    ". Expected long. ", e);
+            return null;
+        }
+    }
+
+    private static Map<String, Object> createAttributes(
+            Long refreshInterval, boolean relaxedLocking) {
+        if (refreshInterval == null && !relaxedLocking) {
+            return emptyMap();
+        } else if (refreshInterval == null) {
+            return singletonMap(RELAXED_LOCKING, (Object) Boolean.valueOf(relaxedLocking));
+        } else if (!relaxedLocking) {
+            return singletonMap(REFRESH_INTERVAL, (Object) refreshInterval);
+        } else {
+            return ImmutableMap.of(
+                    REFRESH_INTERVAL, (Object) refreshInterval,
+                    RELAXED_LOCKING,  (Object) Boolean.valueOf(relaxedLocking));
+        }
+    }
+
+    /**
+     * Auto refresh logic for sessions, which is done to enhance backwards compatibility with
+     * Jackrabbit 2.
+     * <p>
+     * A sessions is automatically refreshed when
+     * <ul>
+     *     <li>it has not been accessed for the number of seconds specified by the
+     *         {@code refreshInterval} parameter,</li>
+     *     <li>an observation event has been delivered to a listener registered from within this
+     *         session,</li>
+     *     <li>an updated occurred through a different session from <em>within the same
+     *         thread.</em></li>
+     * </ul>
+     * In addition a warning is logged once per session if the session is accessed after one
+     * minute of inactivity.
+     */
+    private RefreshStrategy createRefreshStrategy(Long refreshInterval) {
+        if (refreshInterval == null) {
+            return new RefreshStrategy.LogOnce(60);
+        } else {
+            return new RefreshStrategy.Timed(refreshInterval);
+        }
+    }
+
+    private static class RegistrationCallable implements Callable<Registration> {
+        private final SessionStats sessionStats;
+        private final Whiteboard whiteboard;
+
+        public RegistrationCallable(SessionStats sessionStats, Whiteboard whiteboard) {
+            this.sessionStats = sessionStats;
+            this.whiteboard = whiteboard;
+        }
+
+        @Override
+        public Registration call() throws Exception {
+            return WhiteboardUtils.registerMBean(whiteboard, SessionMBean.class,
+                    sessionStats, SessionMBean.TYPE, sessionStats.toString());
+        }
+    }
+}