Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/RemoteValue.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/RemoteValue.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/RemoteValue.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/RemoteValue.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,1431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+
+/**
+ * Represents a value that can be assigned to a property in the repository.
+ * Client of the remote repository provides values as instances of this class.
+ */
+public class RemoteValue {
+
+    /**
+     * A generic interface to represent a supplier of an item.
+     * <p/>
+     * In the specific, it is used by values whose underlying implementation is
+     * an {@code InputStream}. To enable multiple traversals of {@code
+     * InputStream}s, the value is wrapped by this interface to effectively 
have
+     * a factory over the underlying {@code InputStream}.
+     *
+     * @param <T> Type of the item this object is able to create.
+     */
+    public static interface Supplier<T> {
+
+        T get();
+
+    }
+
+    /**
+     * This class helps executing logic that depends on the type of a remote
+     * value. Instead of manually branching code depending on the result of
+     * {@code isText}, {@code isBoolean} and so on, a handler can be 
implemented
+     * to provide different logic for different types.
+     */
+    public static class TypeHandler {
+
+        public void isBinary(Supplier<InputStream> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiBinary(Iterable<Supplier<InputStream>> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isBinaryId(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiBinaryId(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isBoolean(Boolean value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiBoolean(Iterable<Boolean> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isDate(Long value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiDate(Iterable<Long> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isDecimal(BigDecimal value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiDecimal(Iterable<BigDecimal> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isDouble(Double value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiDouble(Iterable<Double> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isLong(Long value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiLong(Iterable<Long> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isName(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiName(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isPath(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiPath(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isReference(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiReference(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isText(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiText(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isUri(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiUri(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isWeakReference(String value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isMultiWeakReference(Iterable<String> value) {
+            throw new IllegalStateException("case not handled");
+        }
+
+        public void isUnknown() {
+            throw new IllegalStateException("case not handled");
+        }
+    }
+
+    /**
+     * Create a remote value of type string.
+     *
+     * @param value The string wrapped by the remote value.
+     * @return A remote value of type string wrapping the provided value.
+     */
+    public static RemoteValue toText(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isText() {
+                return true;
+            }
+
+            @Override
+            public String asText() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type string.
+     *
+     * @param value The collection of strings wrapped by the remote value.
+     * @return A remote multi-value of type string wrapping the provided
+     * collection of strings.
+     */
+    public static RemoteValue toMultiText(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiText() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiText() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type binary.
+     *
+     * @param value The factory of input streams wrapped by the remote value.
+     * @return A remote value of type binary wrapping the provided factory of
+     * input streams.
+     */
+    public static RemoteValue toBinary(final Supplier<InputStream> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isBinary() {
+                return true;
+            }
+
+            @Override
+            public Supplier<InputStream> asBinary() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type binary.
+     *
+     * @param value The collection of factories of input streams wrapped by the
+     *              remote value.
+     * @return A remote multi-value of type binary wrapping the provided
+     * collection of input streams.
+     */
+    public static RemoteValue toMultiBinary(final 
Iterable<Supplier<InputStream>> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiBinary() {
+                return true;
+            }
+
+            @Override
+            public Iterable<Supplier<InputStream>> asMultiBinary() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type binary ID.
+     *
+     * @param value The binary ID wrapped by the remote value.
+     * @return A remote value wrapping the provided binary ID.
+     */
+    public static RemoteValue toBinaryId(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isBinaryId() {
+                return true;
+            }
+
+            @Override
+            public String asBinaryId() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type binary ID.
+     *
+     * @param value The collection of binary IDs wrapped by the remote value.
+     * @return A remote multi-value wrapping the provided collection of binary
+     * IDs.
+     */
+    public static RemoteValue toMultiBinaryId(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiBinaryId() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiBinaryId() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type long.
+     *
+     * @param value The long to wrap in a remote value.
+     * @return A remote value of type long wrapping the provided long value.
+     */
+    public static RemoteValue toLong(final long value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isLong() {
+                return true;
+            }
+
+
+            @Override
+            public Long asLong() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type long.
+     *
+     * @param value The collection of long values to wrap in a remote value.
+     * @return A remote multi-value of type long wrapping the provided
+     * collection of long values.
+     */
+    public static RemoteValue toMultiLong(final Iterable<Long> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiLong() {
+                return true;
+            }
+
+            @Override
+            public Iterable<Long> asMultiLong() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type double.
+     *
+     * @param value The double value to wrap into a remote value.
+     * @return A remote value wrapping the provided remote value.
+     */
+    public static RemoteValue toDouble(final double value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isDouble() {
+                return true;
+            }
+
+            @Override
+            public Double asDouble() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type double.
+     *
+     * @param value The collection of double values to wrap into a remote
+     *              value.
+     * @return A remote multi-value of type double wrapping the provided
+     * collection of double values.
+     */
+    public static RemoteValue toMultiDouble(final Iterable<Double> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiDouble() {
+                return true;
+            }
+
+            @Override
+            public Iterable<Double> asMultiDouble() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type date.
+     *
+     * @param value The date to wrap into a remote value. The date is expressed
+     *              in milliseconds since January 1, 1970, 00:00:00 GMT.
+     * @return A remote value of type date wrapping the provided date.
+     */
+    public static RemoteValue toDate(final long value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isDate() {
+                return true;
+            }
+
+            @Override
+            public Long asDate() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type date.
+     *
+     * @param value The collection of dates to wrap into a remote value. Every
+     *              date is expressed in milliseconds since January 1, 1970,
+     *              00:00:00 GMT.
+     * @return A remote multi-value of type date wrapping the provided
+     * collection of dates.
+     */
+    public static RemoteValue toMultiDate(final Iterable<Long> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiDate() {
+                return true;
+            }
+
+            @Override
+            public Iterable<Long> asMultiDate() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type boolean.
+     *
+     * @param value The boolean value to wrap into a remote value.
+     * @return A remote value wrapping the provided boolean value.
+     */
+    public static RemoteValue toBoolean(final boolean value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isBoolean() {
+                return true;
+            }
+
+            @Override
+            public Boolean asBoolean() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type boolean.
+     *
+     * @param value The collection of boolean values to wrap into a remote
+     *              value.
+     * @return A remote value wrapping the provided collection of boolean
+     * values.
+     */
+    public static RemoteValue toMultiBoolean(final Iterable<Boolean> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiBoolean() {
+                return true;
+            }
+
+            @Override
+            public Iterable<Boolean> asMultiBoolean() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type name.
+     *
+     * @param value The name to wrap into a remote value. A name is represented
+     *              as a string.
+     * @return A remote value of type name wrapping the provided string.
+     */
+    public static RemoteValue toName(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isName() {
+                return true;
+            }
+
+            @Override
+            public String asName() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type name.
+     *
+     * @param value The collection of names to wrap into a remote value. Every
+     *              name is represented by a string.
+     * @return A remote multi-value of type name wrapping the provided
+     * collection of strings.
+     */
+    public static RemoteValue toMultiName(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiName() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiName() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type path.
+     *
+     * @param value The path to wrap into the remote value. A path is
+     *              represented by a string.
+     * @return A remote value of type path wrapping the provided string.
+     */
+    public static RemoteValue toPath(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isPath() {
+                return true;
+            }
+
+            @Override
+            public String asPath() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type path.
+     *
+     * @param value The collection of paths to wrap into a remote value. Every
+     *              path is represented by a string.
+     * @return A remote multi-value of type path wrapping the provided strings.
+     */
+    public static RemoteValue toMultiPath(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiPath() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiPath() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type reference.
+     *
+     * @param value The reference to wrap in a remote value. The reference is
+     *              represented by a string.
+     * @return A remote value of type reference wrapping the provided string.
+     */
+    public static RemoteValue toReference(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isReference() {
+                return true;
+            }
+
+            @Override
+            public String asReference() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type reference.
+     *
+     * @param value The collection of references to wrap in a remote value.
+     *              Every reference is represented by a string.
+     * @return A remote multi-value of type reference wrapping the provided
+     * collection of strings.
+     */
+    public static RemoteValue toMultiReference(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiReference() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiReference() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type weak reference.
+     *
+     * @param value The weak reference to wrap into a remote value. The weak
+     *              reference is represented by a string value.
+     * @return A remote value of type weak reference wrapping the provided
+     * string value.
+     */
+    public static RemoteValue toWeakReference(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isWeakReference() {
+                return true;
+            }
+
+            @Override
+            public String asWeakReference() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type weak reference.
+     *
+     * @param value The collection of weak references to wrap into a remote
+     *              value. Every weak reference is represented by a string.
+     * @return A remote multi-value of type weak reference wrapping the 
provided
+     * collection of strings.
+     */
+    public static RemoteValue toMultiWeakReference(final Iterable<String> 
value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiWeakReference() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiWeakReference() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type URI.
+     *
+     * @param value The string representation of the URI to wrap into a remote
+     *              value.
+     * @return A remote value of type URI wrapping the provided string.
+     */
+    public static RemoteValue toUri(final String value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isUri() {
+                return true;
+            }
+
+            @Override
+            public String asUri() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type URI.
+     *
+     * @param value The collection of URIs to wrap into the remote value. Every
+     *              URI is represented by a string.
+     * @return A remote multi-value of type URI wrapping the provided 
collection
+     * of strings.
+     */
+    public static RemoteValue toMultiUri(final Iterable<String> value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiUri() {
+                return true;
+            }
+
+            @Override
+            public Iterable<String> asMultiUri() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote value of type decimal.
+     *
+     * @param value The decimal to wrap into a remote value.
+     * @return A remote value of type decimal wrapping the provided decimal
+     * value.
+     */
+    public static RemoteValue toDecimal(final BigDecimal value) {
+        return new RemoteValue() {
+
+            @Override
+            public boolean isDecimal() {
+                return true;
+            }
+
+            @Override
+            public BigDecimal asDecimal() {
+                return value;
+            }
+
+        };
+    }
+
+    /**
+     * Create a remote multi-value of type decimal.
+     *
+     * @param value The collection of decimals to wrap into a remote value.
+     * @return A remote multi-value of type decimal wrapping the provided
+     * collection of decimals.
+     */
+    public static RemoteValue toMultiDecimal(final Iterable<BigDecimal> value) 
{
+        return new RemoteValue() {
+
+            @Override
+            public boolean isMultiDecimal() {
+                return true;
+            }
+
+            @Override
+            public Iterable<BigDecimal> asMultiDecimal() {
+                return value;
+            }
+
+        };
+    }
+
+    private RemoteValue() {
+
+    }
+
+    /**
+     * Check if this remote value is of type string.
+     *
+     * @return {@code true} if this remote value is of type string, {@code
+     * false} otherwise.
+     */
+    public boolean isText() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote string value.
+     *
+     * @return The string wrapped by this remote value if this remote value is
+     * of type string, {@code null} otherwise.
+     */
+    public String asText() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type string.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * string, {@code false} otherwise.
+     */
+    public boolean isMultiText() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote string multi-value.
+     *
+     * @return The collection of strings wrapped by this remote value if this
+     * remote value is a multi-value of type string, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiText() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type binary.
+     *
+     * @return {@code true} if this remote value is of type binary, {@code
+     * false} otherwise.
+     */
+    public boolean isBinary() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote boolean value.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * binary, {@code null} otherwise.
+     */
+    public Supplier<InputStream> asBinary() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type binary.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * binary, {@code false} otherwise.
+     */
+    public boolean isMultiBinary() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote binary multi-value.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type binary, {@code null} otherwise.
+     */
+    public Iterable<Supplier<InputStream>> asMultiBinary() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type binary ID.
+     *
+     * @return {@code true} if this remote value is of type binary ID, {@code
+     * false} otherwise.
+     */
+    public boolean isBinaryId() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote binary ID multi-value.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * binary ID, {@code null} otherwise.
+     */
+    public String asBinaryId() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type binary ID.
+     *
+     * @return {@code true} if this remote value is a multi-value of type 
binary
+     * ID, {@code false} otherwise.
+     */
+    public boolean isMultiBinaryId() {
+        return false;
+    }
+
+    /**
+     * Return the value of this remote binary ID multi-value.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type binary ID, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiBinaryId() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type long.
+     *
+     * @return {@code true} if this remote value is of type long, {@code false}
+     * otherwise.
+     */
+    public boolean isLong() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote long multi-value.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * long, {@code null} otherwise.
+     */
+    public Long asLong() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type long.
+     *
+     * @return {@code true} if this value is a multi-value of type long, {@code
+     * false} otherwise.
+     */
+    public boolean isMultiLong() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type long.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type long, {@code null} otherwise.
+     */
+    public Iterable<Long> asMultiLong() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type double.
+     *
+     * @return {@code true} if this remote value is of type long, {@code false}
+     * otherwise.
+     */
+    public boolean isDouble() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type double.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * double, {@code null} otherwise.
+     */
+    public Double asDouble() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type double.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * double, {@code false} otherwise.
+     */
+    public boolean isMultiDouble() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type double.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type double, {@code null} otherwise.
+     */
+    public Iterable<Double> asMultiDouble() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type date.
+     *
+     * @return {@code true} if this remote value is of type date, {@code false}
+     * otherwise.
+     */
+    public boolean isDate() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type date.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * date, {@code null} otherwise.
+     */
+    public Long asDate() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type date.
+     *
+     * @return {@code true} if this remote value is a multi-value of type date,
+     * {@code false} otherwise.
+     */
+    public boolean isMultiDate() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type date.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type date, {@code null} otherwise.
+     */
+    public Iterable<Long> asMultiDate() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type boolean.
+     *
+     * @return {@code true} if this remote value is fo type boolean, {@code
+     * false} otherwise.
+     */
+    public boolean isBoolean() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type boolean.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * boolean, false otherwise.
+     */
+    public Boolean asBoolean() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type boolean.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * boolean, {@code false} otherwise.
+     */
+    public boolean isMultiBoolean() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type boolean.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type boolean, {@code null} otherwise.
+     */
+    public Iterable<Boolean> asMultiBoolean() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type name.
+     *
+     * @return {@code true} if this remote value is of type name, {@code false}
+     * otherwise.
+     */
+    public boolean isName() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type name.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * name, {@code null} otherwise.
+     */
+    public String asName() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type name.
+     *
+     * @return {@code true} if this remote value is a multi-value of type name,
+     * {@code false} otherwise.
+     */
+    public boolean isMultiName() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type name.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type name, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiName() {
+        return null;
+    }
+
+    /**
+     * Check if this value is of type path.
+     *
+     * @return {@code true} if this remote value is of type path, {@code false}
+     * otherwise.
+     */
+    public boolean isPath() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type path.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * path, {@code null} otherwise.
+     */
+    public String asPath() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type path.
+     *
+     * @return {@code true} if this remote value is a multi-value of type path,
+     * {@code false} otherwise.
+     */
+    public boolean isMultiPath() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type path.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type path, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiPath() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type reference.
+     *
+     * @return {@code true} if this remote value is of type reference, {@code
+     * false} otherwise.
+     */
+    public boolean isReference() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type reference.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * reference, {@code null} otherwise.
+     */
+    public String asReference() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type reference.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * reference, {@code false} otherwise.
+     */
+    public boolean isMultiReference() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type reference.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type reference, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiReference() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type weak reference.
+     *
+     * @return {@code true} if this remote value is fo type weak reference,
+     * {@code false} otherwise.
+     */
+    public boolean isWeakReference() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type weak reference.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * weak reference, {@code null} otherwise.
+     */
+    public String asWeakReference() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type weak reference.
+     *
+     * @return {@code true} if this remote value is a multi-value of type weak
+     * reference, {@code false} otherwise.
+     */
+    public boolean isMultiWeakReference() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type weak reference.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type weak reference, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiWeakReference() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type decimal.
+     *
+     * @return {@code true} if this remote value is of type decimal, {@code
+     * false} otherwise.
+     */
+    public boolean isDecimal() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type decimal.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * decimal, {@code null} otherwise.
+     */
+    public BigDecimal asDecimal() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type decimal.
+     *
+     * @return {@code true} if this remote value is a multi-value of type
+     * decimal, {@code false} otherwise.
+     */
+    public boolean isMultiDecimal() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type decimal.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type decimal, {@code null} otherwise.
+     */
+    public Iterable<BigDecimal> asMultiDecimal() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is of type URI.
+     *
+     * @return {@code true} if this remote value is of type URI, {@code false}
+     * otherwise.
+     */
+    public boolean isUri() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote value of type URI.
+     *
+     * @return The value of this remote value if this remote value is of type
+     * URI, {@code null} otherwise.
+     */
+    public String asUri() {
+        return null;
+    }
+
+    /**
+     * Check if this remote value is a multi-value of type URI.
+     *
+     * @return {@code true} if this remote value is a multi-value of type URI,
+     * {@code false} otherwise.
+     */
+    public boolean isMultiUri() {
+        return false;
+    }
+
+    /**
+     * Read the value of this remote multi-value of type URI.
+     *
+     * @return The value of this remote value if this remote value is a
+     * multi-value of type URI, {@code null} otherwise.
+     */
+    public Iterable<String> asMultiUri() {
+        return null;
+    }
+
+    /**
+     * Calls a method of the provided handler according to the type of this
+     * remote value.
+     *
+     * @param handler Handler containing logic to be executing according to the
+     *                type of this remote value.
+     */
+    public void whenType(TypeHandler handler) {
+        if (isBinary()) {
+            handler.isBinary(asBinary());
+            return;
+        }
+
+        if (isMultiBinary()) {
+            handler.isMultiBinary(asMultiBinary());
+            return;
+        }
+
+        if (isBinaryId()) {
+            handler.isBinaryId(asBinaryId());
+            return;
+        }
+
+        if (isMultiBinaryId()) {
+            handler.isMultiBinaryId(asMultiBinaryId());
+            return;
+        }
+
+        if (isBoolean()) {
+            handler.isBoolean(asBoolean());
+            return;
+        }
+
+        if (isMultiBoolean()) {
+            handler.isMultiBoolean(asMultiBoolean());
+            return;
+        }
+
+        if (isDate()) {
+            handler.isDate(asDate());
+            return;
+        }
+
+        if (isMultiDate()) {
+            handler.isMultiDate(asMultiDate());
+            return;
+        }
+
+        if (isDecimal()) {
+            handler.isDecimal(asDecimal());
+            return;
+        }
+
+        if (isMultiDecimal()) {
+            handler.isMultiDecimal(asMultiDecimal());
+            return;
+        }
+
+        if (isDouble()) {
+            handler.isDouble(asDouble());
+            return;
+        }
+
+        if (isMultiDouble()) {
+            handler.isMultiDouble(asMultiDouble());
+            return;
+        }
+
+        if (isLong()) {
+            handler.isLong(asLong());
+            return;
+        }
+
+        if (isMultiLong()) {
+            handler.isMultiLong(asMultiLong());
+            return;
+        }
+
+        if (isName()) {
+            handler.isName(asName());
+            return;
+        }
+
+        if (isMultiName()) {
+            handler.isMultiName(asMultiName());
+            return;
+        }
+
+        if (isPath()) {
+            handler.isPath(asPath());
+            return;
+        }
+
+        if (isMultiPath()) {
+            handler.isMultiPath(asMultiPath());
+            return;
+        }
+
+        if (isReference()) {
+            handler.isReference(asReference());
+            return;
+        }
+
+        if (isMultiReference()) {
+            handler.isMultiReference(asMultiReference());
+            return;
+        }
+
+        if (isText()) {
+            handler.isText(asText());
+            return;
+        }
+
+        if (isMultiText()) {
+            handler.isMultiText(asMultiText());
+            return;
+        }
+
+        if (isUri()) {
+            handler.isUri(asUri());
+            return;
+        }
+
+        if (isMultiUri()) {
+            handler.isMultiUri(asMultiUri());
+            return;
+        }
+
+        if (isWeakReference()) {
+            handler.isWeakReference(asWeakReference());
+            return;
+        }
+
+        if (isMultiWeakReference()) {
+            handler.isMultiWeakReference(asMultiWeakReference());
+            return;
+        }
+
+        handler.isUnknown();
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperation.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperation.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperation.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AddContentRemoteOperation.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.remote.RemoteCommitException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class AddContentRemoteOperation implements ContentRemoteOperation {
+
+    private static final Logger logger = 
LoggerFactory.getLogger(AddContentRemoteOperation.class);
+
+    private final String path;
+
+    public AddContentRemoteOperation(String path) {
+        this.path = path;
+    }
+
+    @Override
+    public void apply(Root root) throws RemoteCommitException {
+        logger.debug("performing 'add' operation on path={}", path);
+
+        Tree tree = root.getTree(path);
+
+        if (tree.exists()) {
+            throw new RemoteCommitException("node already exists");
+        }
+
+        Tree parent = tree.getParent();
+
+        if (!parent.exists()) {
+            throw new RemoteCommitException("parent node does not exist");
+        }
+
+        parent.addChild(tree.getName());
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AggregateContentRemoteOperation.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AggregateContentRemoteOperation.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AggregateContentRemoteOperation.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/AggregateContentRemoteOperation.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.remote.RemoteCommitException;
+
+import java.util.List;
+
+class AggregateContentRemoteOperation implements ContentRemoteOperation {
+
+    private final List<ContentRemoteOperation> operations;
+
+    public AggregateContentRemoteOperation(List<ContentRemoteOperation> 
operations) {
+        this.operations = operations;
+    }
+
+    @Override
+    public void apply(Root root) throws RemoteCommitException {
+        for (ContentRemoteOperation operation : operations) {
+            operation.apply(root);
+        }
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/BasicContentRemoteCredentials.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/BasicContentRemoteCredentials.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/BasicContentRemoteCredentials.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/BasicContentRemoteCredentials.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.remote.RemoteLoginException;
+
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.SimpleCredentials;
+import javax.security.auth.login.LoginException;
+
+class BasicContentRemoteCredentials implements ContentRemoteCredentials {
+
+    private final String user;
+
+    private final char[] password;
+
+    public BasicContentRemoteCredentials(String user, char[] password) {
+        this.user = user;
+        this.password = password;
+    }
+
+    @Override
+    public ContentSession login(ContentRepository repository) throws 
RemoteLoginException {
+        ContentSession session;
+
+        try {
+            session = repository.login(new SimpleCredentials(user, password), 
null);
+        } catch (LoginException e) {
+            throw new RemoteLoginException("unable to login", e);
+        } catch (NoSuchWorkspaceException e) {
+            throw new RemoteLoginException("unable to use the default 
workspace", e);
+        }
+
+        return session;
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaries.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaries.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaries.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaries.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.api.Blob;
+
+import java.util.Map;
+import java.util.UUID;
+
+class ContentRemoteBinaries {
+
+    private Map<String, Blob> binaries;
+
+    public ContentRemoteBinaries() {
+        binaries = Maps.newHashMap();
+    }
+
+    public String put(Blob blob) {
+        String binaryId = UUID.randomUUID().toString();
+
+        binaries.put(binaryId, blob);
+
+        return binaryId;
+    }
+
+    public Blob get(String binaryId) {
+        return binaries.get(binaryId);
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaryId.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaryId.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaryId.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteBinaryId.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.remote.RemoteBinaryId;
+
+class ContentRemoteBinaryId implements RemoteBinaryId {
+
+    private final String reference;
+
+    private final Blob blob;
+
+    public ContentRemoteBinaryId(String reference, Blob blob) {
+        this.reference = reference;
+        this.blob = blob;
+    }
+
+    public Blob asBlob() {
+        return blob;
+    }
+
+    @Override
+    public String asString() {
+        return reference;
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteCredentials.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteCredentials.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteCredentials.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteCredentials.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.remote.RemoteCredentials;
+import org.apache.jackrabbit.oak.remote.RemoteLoginException;
+
+interface ContentRemoteCredentials extends RemoteCredentials {
+
+    ContentSession login(ContentRepository repository) throws 
RemoteLoginException;
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteInputStream.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteInputStream.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteInputStream.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteInputStream.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.remote.RemoteBinaryFilters;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+class ContentRemoteInputStream extends InputStream {
+
+    private final InputStream stream;
+
+    private final long start;
+
+    private final long count;
+
+    private long index = 0;
+
+    public ContentRemoteInputStream(InputStream stream, RemoteBinaryFilters 
filters) {
+        this.stream = stream;
+
+        long startFilter = filters.getStart();
+
+        if (startFilter > 0) {
+            this.start = startFilter;
+        } else {
+            this.start = 0;
+        }
+
+        this.count = filters.getCount();
+    }
+
+    @Override
+    public int read() throws IOException {
+        while (index < start - 1) {
+            long skipped = stream.skip(start - index);
+
+            if (skipped <= 0) {
+                return -1;
+            }
+
+            index = index + skipped;
+        }
+
+        if (count >= 0 && index >= start + count) {
+            return -1;
+        }
+
+        int result = stream.read();
+
+        index = index + 1;
+
+        return result;
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteOperation.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteOperation.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteOperation.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteOperation.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.remote.RemoteCommitException;
+import org.apache.jackrabbit.oak.remote.RemoteOperation;
+
+interface ContentRemoteOperation extends RemoteOperation {
+
+    void apply(Root root) throws RemoteCommitException;
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepository.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepository.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepository.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRepository.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.remote.RemoteCredentials;
+import org.apache.jackrabbit.oak.remote.RemoteLoginException;
+import org.apache.jackrabbit.oak.remote.RemoteRepository;
+import org.apache.jackrabbit.oak.remote.RemoteSession;
+
+import java.util.Set;
+
+public class ContentRemoteRepository implements RemoteRepository {
+
+    private final ContentRepository contentRepository;
+
+    private final ContentRemoteRevisions contentRemoteRevisions;
+
+    private final ContentRemoteBinaries contentRemoteBinaries;
+
+    public ContentRemoteRepository(ContentRepository contentRepository) {
+        this.contentRemoteRevisions = new ContentRemoteRevisions();
+        this.contentRemoteBinaries = new ContentRemoteBinaries();
+        this.contentRepository = contentRepository;
+    }
+
+    @Override
+    public RemoteCredentials createBasicCredentials(final String user, final 
char[] password) {
+        return new BasicContentRemoteCredentials(user, password);
+    }
+
+    @Override
+    public RemoteCredentials createImpersonationCredentials(Set<String> 
principals) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    public RemoteSession login(RemoteCredentials remoteCredentials) throws 
RemoteLoginException {
+        ContentRemoteCredentials contentRemoteCredentials = null;
+
+        if (remoteCredentials instanceof ContentRemoteCredentials) {
+            contentRemoteCredentials = (ContentRemoteCredentials) 
remoteCredentials;
+        }
+
+        if (contentRemoteCredentials == null) {
+            throw new IllegalArgumentException("invalid credentials");
+        }
+
+        Thread thread = Thread.currentThread();
+
+        ClassLoader loader = thread.getContextClassLoader();
+
+        thread.setContextClassLoader(Oak.class.getClassLoader());
+
+        ContentSession session;
+
+        try {
+            session = contentRemoteCredentials.login(contentRepository);
+        } finally {
+            thread.setContextClassLoader(loader);
+        }
+
+        return new ContentRemoteSession(session, contentRemoteRevisions, 
contentRemoteBinaries);
+    }
+
+}
\ No newline at end of file

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResult.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResult.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResult.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResult.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyValue;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.remote.RemoteResult;
+import org.apache.jackrabbit.oak.remote.RemoteValue;
+import org.apache.jackrabbit.util.ISO8601;
+
+import javax.jcr.PropertyType;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+class ContentRemoteResult implements RemoteResult {
+
+    private final ContentRemoteBinaries binaries;
+
+    private final ResultRow row;
+
+    public ContentRemoteResult(ContentRemoteBinaries binaries, ResultRow row) {
+        this.binaries = binaries;
+        this.row = row;
+    }
+
+    @Override
+    public RemoteValue getColumnValue(String column) {
+        return toRemoteValue(row.getValue(column));
+    }
+
+    private RemoteValue toRemoteValue(PropertyValue value) {
+        if (value == null) {
+            return null;
+        }
+        
+        Type<?> type = value.getType();
+
+        if (type.isArray()) {
+            return toMultiRemoteValue(value);
+        } else {
+            return toSingleRemoteValue(value);
+        }
+    }
+
+    private RemoteValue toSingleRemoteValue(PropertyValue value) {
+        Type<?> type = value.getType();
+
+        switch (type.tag()) {
+            case PropertyType.STRING:
+                return RemoteValue.toText(value.getValue(Type.STRING));
+            case PropertyType.BINARY:
+                return 
RemoteValue.toBinaryId(binaries.put(value.getValue(Type.BINARY)));
+            case PropertyType.LONG:
+                return RemoteValue.toLong(value.getValue(Type.LONG));
+            case PropertyType.DOUBLE:
+                return RemoteValue.toDouble(value.getValue(Type.DOUBLE));
+            case PropertyType.DATE:
+                return 
RemoteValue.toDate(ISO8601.parse(value.getValue(Type.DATE)).getTimeInMillis());
+            case PropertyType.BOOLEAN:
+                return RemoteValue.toBoolean(value.getValue(Type.BOOLEAN));
+            case PropertyType.NAME:
+                return RemoteValue.toName(value.getValue(Type.NAME));
+            case PropertyType.PATH:
+                return RemoteValue.toPath(value.getValue(Type.PATH));
+            case PropertyType.REFERENCE:
+                return RemoteValue.toReference(value.getValue(Type.REFERENCE));
+            case PropertyType.WEAKREFERENCE:
+                return 
RemoteValue.toWeakReference(value.getValue(Type.WEAKREFERENCE));
+            case PropertyType.URI:
+                return RemoteValue.toUri(value.getValue(Type.URI));
+            case PropertyType.DECIMAL:
+                return RemoteValue.toDecimal(value.getValue(Type.DECIMAL));
+        }
+
+        throw new IllegalStateException("type not supported");
+    }
+
+    private RemoteValue toMultiRemoteValue(PropertyValue value) {
+        Type<?> type = value.getType();
+
+        switch (type.tag()) {
+            case PropertyType.STRING:
+                return RemoteValue.toMultiText(value.getValue(Type.STRINGS));
+            case PropertyType.BINARY:
+                return RemoteValue.toMultiBinaryId(readBinaryValues(value));
+            case PropertyType.LONG:
+                return RemoteValue.toMultiLong(value.getValue(Type.LONGS));
+            case PropertyType.DOUBLE:
+                return RemoteValue.toMultiDouble(value.getValue(Type.DOUBLES));
+            case PropertyType.DATE:
+                return RemoteValue.toMultiDate(readDateValues(value));
+            case PropertyType.BOOLEAN:
+                return 
RemoteValue.toMultiBoolean(value.getValue(Type.BOOLEANS));
+            case PropertyType.NAME:
+                return RemoteValue.toMultiName(value.getValue(Type.NAMES));
+            case PropertyType.PATH:
+                return RemoteValue.toMultiPath(value.getValue(Type.PATHS));
+            case PropertyType.REFERENCE:
+                return 
RemoteValue.toMultiReference(value.getValue(Type.REFERENCES));
+            case PropertyType.WEAKREFERENCE:
+                return 
RemoteValue.toMultiWeakReference(value.getValue(Type.WEAKREFERENCES));
+            case PropertyType.URI:
+                return RemoteValue.toMultiUri(value.getValue(Type.URIS));
+            case PropertyType.DECIMAL:
+                return 
RemoteValue.toMultiDecimal(value.getValue(Type.DECIMALS));
+        }
+
+        throw new IllegalStateException("type not supported");
+    }
+
+    private Iterable<String> readBinaryValues(PropertyValue value) {
+        List<String> result = newArrayList();
+
+        for (Blob blob : value.getValue(Type.BINARIES)) {
+            result.add(binaries.put(blob));
+        }
+
+        return result;
+    }
+
+    private Iterable<Long> readDateValues(PropertyValue value) {
+        List<Long> result = newArrayList();
+
+        for (String string : value.getValue(Type.DATES)) {
+            result.add(ISO8601.parse(string).getTimeInMillis());
+        }
+
+        return result;
+    }
+
+    @Override
+    public String getSelectorPath(String selector) {
+        return row.getPath(selector);
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResults.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResults.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResults.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteResults.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Result;
+import org.apache.jackrabbit.oak.api.ResultRow;
+import org.apache.jackrabbit.oak.remote.RemoteResult;
+import org.apache.jackrabbit.oak.remote.RemoteResults;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+class ContentRemoteResults implements RemoteResults {
+
+    private final ContentRemoteBinaries binaries;
+
+    private final Result results;
+
+    public ContentRemoteResults(ContentRemoteBinaries binaries, Result 
results) {
+        this.binaries = binaries;
+        this.results = results;
+    }
+
+    @Override
+    public long getTotal() {
+        return results.getSize();
+    }
+
+    @Override
+    public Iterable<String> getColumns() {
+        return Arrays.asList(results.getColumnNames());
+    }
+
+    @Override
+    public Iterable<String> getSelectors() {
+        return Arrays.asList(results.getSelectorNames());
+    }
+
+    @Override
+    public Iterator<RemoteResult> iterator() {
+        return getResults().iterator();
+    }
+
+    private Iterable<RemoteResult> getResults() {
+        List<RemoteResult> results = newArrayList();
+
+        for (ResultRow row : this.results.getRows()) {
+            results.add(new ContentRemoteResult(binaries, row));
+        }
+
+        return results;
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevision.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevision.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevision.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevision.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import org.apache.jackrabbit.oak.api.Root;
+import org.apache.jackrabbit.oak.remote.RemoteRevision;
+
+class ContentRemoteRevision implements RemoteRevision {
+
+    private final String id;
+
+    private final Root root;
+
+    public ContentRemoteRevision(String id, Root root) {
+        this.id = id;
+        this.root = root;
+    }
+
+    @Override
+    public String asString() {
+        return id;
+    }
+
+    public Root getRoot() {
+        return root;
+    }
+
+}

Added: 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevisions.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevisions.java?rev=1684861&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevisions.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/content/ContentRemoteRevisions.java
 Thu Jun 11 12:09:15 2015
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.oak.remote.content;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
+import org.apache.jackrabbit.oak.api.AuthInfo;
+import org.apache.jackrabbit.oak.api.Root;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+class ContentRemoteRevisions {
+
+    private class Key {
+
+        private final String revisionId;
+
+        private final Set<Principal> principals;
+
+        private final String user;
+
+        private Key(AuthInfo authInfo, String revisionId) {
+            this.user = authInfo.getUserID();
+            this.principals = authInfo.getPrincipals();
+            this.revisionId = revisionId;
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            if (object == null) {
+                return false;
+            }
+
+            if (object == this) {
+                return true;
+            }
+
+            if (getClass() != object.getClass()) {
+                return false;
+            }
+
+            Key other = (Key) object;
+
+            if (!Objects.equal(revisionId, other.revisionId)) {
+                return false;
+            }
+
+            if (!Objects.equal(user, other.user)) {
+                return false;
+            }
+
+            if (!Objects.equal(principals, other.principals)) {
+                return false;
+            }
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(revisionId, user, principals);
+        }
+
+    }
+
+    private Map<Key, Root> roots;
+
+    public ContentRemoteRevisions() {
+        this.roots = Maps.newHashMap();
+    }
+
+    private Key key(AuthInfo authInfo, String revisionId) {
+        return new Key(authInfo, revisionId);
+    }
+
+    public Root get(AuthInfo authInfo, String revisionId) {
+        return roots.get(key(authInfo, revisionId));
+    }
+
+    public String put(AuthInfo authInfo, Root root) {
+        String revisionId = UUID.randomUUID().toString();
+
+        roots.put(key(authInfo, revisionId), root);
+
+        return revisionId;
+    }
+
+}


Reply via email to