Author: chetanm
Date: Thu Jul 20 12:58:02 2017
New Revision: 1802479
URL: http://svn.apache.org/viewvc?rev=1802479&view=rev
Log:
OAK-6476 - Support deserializing json as NodeState
Added:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java
(with props)
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java
(with props)
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java
(with props)
jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java
(with props)
Added:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java?rev=1802479&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java
(added)
+++
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java
Thu Jul 20 12:58:02 2017
@@ -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.json;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.plugins.memory.ArrayBasedBlob;
+import org.apache.jackrabbit.util.Base64;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class Base64BlobSerializer extends BlobSerializer implements
BlobDeserializer {
+ private final int maxSize;
+
+ public Base64BlobSerializer() {
+ this((int)FileUtils.ONE_MB);
+ }
+
+ public Base64BlobSerializer(int maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ @Override
+ public String serialize(Blob blob) {
+ checkArgument(blob.length() < maxSize, "Cannot serialize Blob of size
[%s] " +
+ "which is more than allowed maxSize of [%s]", blob.length(),
maxSize);
+ try {
+ try (InputStream is = blob.getNewStream()) {
+ StringWriter writer = new StringWriter();
+ Base64.encode(is, writer);
+ return writer.toString();
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public Blob deserialize(String value) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ StringReader reader = new StringReader(value);
+ try {
+ Base64.decode(reader, baos);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return new ArrayBasedBlob(baos.toByteArray());
+ }
+}
Propchange:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/Base64BlobSerializer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java?rev=1802479&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java
(added)
+++
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java
Thu Jul 20 12:58:02 2017
@@ -0,0 +1,27 @@
+/*
+ * 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.json;
+
+import org.apache.jackrabbit.oak.api.Blob;
+
+public interface BlobDeserializer {
+
+ Blob deserialize(String value);
+}
Propchange:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/BlobDeserializer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java?rev=1802479&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java
(added)
+++
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java
Thu Jul 20 12:58:02 2017
@@ -0,0 +1,185 @@
+/*
+ * 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.json;
+
+import java.util.List;
+
+import javax.jcr.PropertyType;
+
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.json.JsopReader;
+import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
+import org.apache.jackrabbit.oak.plugins.memory.BinaryPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.BooleanPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.DoublePropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.LongPropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.plugins.memory.StringPropertyState;
+import org.apache.jackrabbit.oak.plugins.value.Conversions;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
+import static java.util.Collections.emptyList;
+import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static
org.apache.jackrabbit.oak.plugins.memory.PropertyStates.createProperty;
+
+public class JsonDeserializer {
+ private final BlobDeserializer blobHandler;
+ private final NodeBuilder builder;
+
+ public JsonDeserializer(BlobDeserializer blobHandler, NodeBuilder builder)
{
+ this.blobHandler = blobHandler;
+ this.builder = builder;
+ }
+
+ public JsonDeserializer(BlobDeserializer blobHandler) {
+ this(blobHandler, EMPTY_NODE.builder());
+ }
+
+ public NodeState deserialize(String json){
+ JsopReader reader = new JsopTokenizer(json);
+ reader.read('{');
+ NodeState state = deserialize(reader);
+ reader.read(JsopReader.END);
+ return state;
+ }
+
+ public NodeState deserialize(JsopReader reader){
+ readNode(reader, builder);
+ reader.read('}');
+ return builder.getNodeState();
+ }
+
+ private void readNode(JsopReader reader, NodeBuilder builder) {
+ do {
+ String key = reader.readString();
+ reader.read(':');
+ if (reader.matches('{')) {
+ readNode(reader, builder.child(key));
+ reader.read('}');
+ } else if (reader.matches('[')){
+ builder.setProperty(readArrayProperty(key, reader));
+ } else {
+ builder.setProperty(readProperty(key, reader));
+ }
+ } while (reader.matches(','));
+ }
+
+ /**
+ * Read a {@code PropertyState} from a {@link JsopReader}
+ * @param name The name of the property state
+ * @param reader The reader
+ * @return new property state
+ */
+ private PropertyState readProperty(String name, JsopReader reader) {
+ if (reader.matches(JsopReader.NUMBER)) {
+ String number = reader.getToken();
+ try {
+ return new LongPropertyState(name, Long.parseLong(number));
+ } catch (NumberFormatException e) {
+ return new DoublePropertyState(name,
Double.parseDouble(number));
+ }
+ } else if (reader.matches(JsopReader.TRUE)) {
+ return BooleanPropertyState.booleanProperty(name, true);
+ } else if (reader.matches(JsopReader.FALSE)) {
+ return BooleanPropertyState.booleanProperty(name, false);
+ } else if (reader.matches(JsopReader.STRING)) {
+ String jsonString = reader.getToken();
+ if (jsonString.startsWith(TypeCodes.EMPTY_ARRAY)) {
+ int type = PropertyType.valueFromName(
+ jsonString.substring(TypeCodes.EMPTY_ARRAY.length()));
+ return PropertyStates.createProperty(
+ name, emptyList(), Type.fromTag(type, true));
+ }
+ int split = TypeCodes.split(jsonString);
+ if (split != -1) {
+ int type = TypeCodes.decodeType(split, jsonString);
+ String value = TypeCodes.decodeName(split, jsonString);
+ if (type == PropertyType.BINARY) {
+ return BinaryPropertyState.binaryProperty(
+ name, blobHandler.deserialize(value));
+ } else {
+ return createProperty(name, value, type);
+ }
+ } else {
+ return StringPropertyState.stringProperty(
+ name, jsonString);
+ }
+ } else {
+ throw new IllegalArgumentException("Unexpected token: " +
reader.getToken());
+ }
+ }
+
+ /**
+ * Read a multi valued {@code PropertyState} from a {@link JsopReader}
+ * @param name The name of the property state
+ * @param reader The reader
+ * @return new property state
+ */
+ private PropertyState readArrayProperty(String name, JsopReader reader) {
+ int type = PropertyType.STRING;
+ List<Object> values = Lists.newArrayList();
+ while (!reader.matches(']')) {
+ if (reader.matches(JsopReader.NUMBER)) {
+ String number = reader.getToken();
+ try {
+ type = PropertyType.LONG;
+ values.add(Long.parseLong(number));
+ } catch (NumberFormatException e) {
+ type = PropertyType.DOUBLE;
+ values.add(Double.parseDouble(number));
+ }
+ } else if (reader.matches(JsopReader.TRUE)) {
+ type = PropertyType.BOOLEAN;
+ values.add(true);
+ } else if (reader.matches(JsopReader.FALSE)) {
+ type = PropertyType.BOOLEAN;
+ values.add(false);
+ } else if (reader.matches(JsopReader.STRING)) {
+ String jsonString = reader.getToken();
+ int split = TypeCodes.split(jsonString);
+ if (split != -1) {
+ type = TypeCodes.decodeType(split, jsonString);
+ String value = TypeCodes.decodeName(split, jsonString);
+ if (type == PropertyType.BINARY) {
+ values.add(blobHandler.deserialize(value));
+ } else if (type == PropertyType.DOUBLE) {
+ values.add(Conversions.convert(value).toDouble());
+ } else if (type == PropertyType.DECIMAL) {
+ values.add(Conversions.convert(value).toDecimal());
+ } else {
+ values.add(value);
+ }
+ } else {
+ type = PropertyType.STRING;
+ values.add(jsonString);
+ }
+ } else {
+ throw new IllegalArgumentException("Unexpected token: " +
reader.getToken());
+ }
+ reader.matches(',');
+ }
+ return createProperty(name, values, Type.fromTag(type, true));
+ }
+
+
+}
Propchange:
jackrabbit/oak/trunk/oak-store-spi/src/main/java/org/apache/jackrabbit/oak/json/JsonDeserializer.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java?rev=1802479&view=auto
==============================================================================
---
jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java
(added)
+++
jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java
Thu Jul 20 12:58:02 2017
@@ -0,0 +1,122 @@
+/*
+ * 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.json;
+
+import java.util.Collections;
+import java.util.Random;
+
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.json.JsopBuilder;
+import org.apache.jackrabbit.oak.plugins.memory.ArrayBasedBlob;
+import org.apache.jackrabbit.oak.spi.state.EqualsDiff;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.junit.Test;
+
+import static
org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+public class JsonDeserializerTest {
+ private Base64BlobSerializer blobHandler = new Base64BlobSerializer();
+ private Random rnd = new Random();
+
+ @Test
+ public void basicStuff() throws Exception{
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("a").setProperty("foo", "bar");
+ builder.child("b").setProperty("foo", 1);
+ assertDeserialization(builder);
+ }
+
+ @Test
+ public void variousPropertyTypes() throws Exception{
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("a").setProperty("foo", 10);
+ builder.child("a").setProperty("foo2", "bar");
+ builder.child("a").setProperty("foo3", true);
+ builder.child("a").setProperty("foo4", false);
+ builder.child("a").setProperty("foo5", 1.1);
+ builder.child("a").setProperty("foo6", "nt:base", Type.NAME);
+ builder.child("a").child("b").setProperty("foo",
Lists.newArrayList(1L,2L,3L), Type.LONGS);
+ builder.child("a").child("b").setProperty("foo2",
Lists.newArrayList("x", "y", "z"), Type.STRINGS);
+ builder.child("a").child("b").setProperty("foo3",
Lists.newArrayList(true, false), Type.BOOLEANS);
+ builder.child("a").child("b").setProperty("foo4",
Lists.newArrayList(1.1, 1.2), Type.DOUBLES);
+ builder.child("a").child(":c").setProperty("foo", "bar");
+
+ assertDeserialization(builder);
+ }
+
+ @Test
+ public void emptyProperty() throws Exception{
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("a").setProperty("foo", Collections.emptyList(),
Type.NAMES);
+ assertDeserialization(builder);
+ }
+
+ @Test
+ public void binaryProperty() throws Exception{
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("a").setProperty("foo", createBlob(100));
+ builder.child("b").setProperty("foo",
Lists.newArrayList(createBlob(200), createBlob(300)), Type.BINARIES);
+ assertDeserialization(builder);
+ }
+
+ @Test
+ public void singleProperty() throws Exception{
+ NodeBuilder builder = EMPTY_NODE.builder();
+ builder.child("a").setProperty("foo3", 1.1);
+ //builder.child("a").setProperty("foo3", Lists.newArrayList(true,
false), Type.BOOLEANS);
+
+ assertDeserialization(builder);
+
+ }
+
+ private Blob createBlob(int length) {
+ return new ArrayBasedBlob(randomBytes(length));
+ }
+
+ private byte[] randomBytes(int size) {
+ byte[] data = new byte[size];
+ rnd.nextBytes(data);
+ return data;
+ }
+
+ private void assertDeserialization(NodeBuilder builder) {
+ NodeState nodeState = builder.getNodeState();
+ String json = serialize(nodeState);
+ NodeState nodeState2 = deserialize(json);
+ assertTrue(EqualsDiff.equals(nodeState, nodeState2));
+ }
+
+ private NodeState deserialize(String json) {
+ JsonDeserializer deserializer = new JsonDeserializer(blobHandler);
+ return deserializer.deserialize(json);
+ }
+
+ private String serialize(NodeState nodeState){
+ JsopBuilder json = new JsopBuilder();
+ new JsonSerializer(json, blobHandler).serialize(nodeState);
+ return json.toString();
+ }
+
+}
\ No newline at end of file
Propchange:
jackrabbit/oak/trunk/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/json/JsonDeserializerTest.java
------------------------------------------------------------------------------
svn:eol-style = native