http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/JsonNumberTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonNumberTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonNumberTest.java index b610995..2a2a12d 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonNumberTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonNumberTest.java @@ -18,17 +18,47 @@ */ package org.apache.johnzon.core; -import java.math.BigInteger; +import org.junit.Assert; +import org.junit.Test; import javax.json.Json; import javax.json.JsonArray; +import javax.json.JsonNumber; +import java.io.StringReader; +import java.io.StringWriter; +import java.math.BigInteger; -import org.junit.Assert; -import org.junit.Test; - +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.fail; public class JsonNumberTest { + @Test + public void nonZeroFractional() { + final JsonNumber number = Json.createArrayBuilder() + .add(12345.6489) + .build() + .getJsonNumber(0); + try { + number.intValueExact(); + fail(); + } catch (final ArithmeticException ae) { + // ok + } + try { + number.longValueExact(); + fail(); + } catch (final ArithmeticException ae) { + // ok + } + } + @Test + public void equals() { + final JsonNumber a = Json.createObjectBuilder().add("a", 1).build().getJsonNumber("a"); + final JsonNumber b = Json.createObjectBuilder().add("b", 1.1).build().getJsonNumber("b"); + assertFalse(a.equals(b)); + assertFalse(b.equals(a)); + } @Test(expected=ArithmeticException.class) public void testBigIntegerExact() { @@ -48,4 +78,21 @@ public class JsonNumberTest { } + @Test + public void testBigIntegerButFromJustALongTooLong() { + final StringWriter writer = new StringWriter(); + Json.createGenerator(writer).writeStartObject().write("value", new BigInteger("10002000000000000000")).writeEnd().close(); + final String asJson = writer.toString(); + final JsonNumber jsonNumber = Json.createReader(new StringReader(asJson)).readObject().getJsonNumber("value"); + Assert.assertEquals(new BigInteger("10002000000000000000"), jsonNumber.bigIntegerValue()); + } + + @Test + public void testHashCode() { + JsonNumber a = Json.createObjectBuilder().add("a", 1).build().getJsonNumber("a"); + JsonNumber b = Json.createObjectBuilder().add("b", 1.1).build().getJsonNumber("b"); + + Assert.assertEquals(a.hashCode(), a.bigDecimalValue().hashCode()); + Assert.assertEquals(b.hashCode(), b.bigDecimalValue().hashCode()); + } }
http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java index 098967e..7ca990d 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectBuilderImplTest.java @@ -21,15 +21,29 @@ package org.apache.johnzon.core; import static org.junit.Assert.assertEquals; import javax.json.Json; +import javax.json.JsonObject; import javax.json.JsonObjectBuilder; import org.junit.Test; public class JsonObjectBuilderImplTest { @Test - public void build() { + public void testBuild() { final JsonObjectBuilder builder = Json.createObjectBuilder(); builder.add("a", "b"); - assertEquals("{\"a\":\"b\"}", builder.build().toString()); + JsonObject jsonObject = builder.build(); + assertEquals("{\"a\":\"b\"}", jsonObject.toString()); + } + + @Test(expected = NullPointerException.class) + public void testNullCheckValue() { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + builder.add("a", (Integer) null); + } + + @Test(expected = NullPointerException.class) + public void testNullCheckName() { + final JsonObjectBuilder builder = Json.createObjectBuilder(); + builder.add(null, "b"); } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectImplTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectImplTest.java index e7b5c06..1557070 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectImplTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonObjectImplTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import javax.json.Json; import javax.json.JsonObject; @@ -32,6 +33,22 @@ import org.junit.Test; public class JsonObjectImplTest { @Test + public void boolErrors() { + { + final JsonObject val = Json.createObjectBuilder().add("a", true).build(); + assertTrue(val.getBoolean("a")); + } + { + final JsonObject val = Json.createObjectBuilder().add("a", "wrong").build(); + try { + val.getBoolean("a"); + fail(); + } catch (final ClassCastException cce) { + // ok + } + } + } + @Test public void objectToString() { final JsonObjectBuilder ob = Json.createObjectBuilder(); ob.add("a", new JsonStringImpl("b")); http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/JsonParserTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonParserTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonParserTest.java index e035789..2e0df04 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonParserTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonParserTest.java @@ -229,7 +229,7 @@ public class JsonParserTest { ob.add("d", ab); - final JsonParser parser = Json.createParserFactory(Collections.<String, Object>emptyMap()).createParser(ob.build()); + final JsonParser parser = Json.createParserFactory(Collections.EMPTY_MAP).createParser(ob.build()); assertNotNull(parser); assertSimple(parser); } @@ -247,18 +247,19 @@ public class JsonParserTest { assertNotNull(parser); assertSimple(parser); } - + @Test public void simpleUTF16LE() { final JsonParser parser = Json.createParserFactory(null).createParser(Thread.currentThread() - .getContextClassLoader().getResourceAsStream("json/simple_utf16le.json"),UTF_16LE); + .getContextClassLoader().getResourceAsStream("json/simple_utf16le.json"), UTF_16LE); assertNotNull(parser); assertSimple(parser); } @Test public void simpleUTF16LEAutoDetect() { - final JsonParser parser = Json.createParserFactory(null).createParser(Thread.currentThread().getContextClassLoader().getResourceAsStream("json/simple_utf16le.json")); + final JsonParser parser = Json.createParserFactory(null).createParser(Thread.currentThread(). + getContextClassLoader().getResourceAsStream("json/simple_utf16le.json")); assertNotNull(parser); assertSimple(parser); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java index e9d3d74..fcb8182 100644 --- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java @@ -42,13 +42,13 @@ import javax.json.JsonReaderFactory; import javax.json.JsonString; import javax.json.JsonStructure; import javax.json.JsonValue; +import javax.json.stream.JsonParsingException; import org.junit.Test; public class JsonReaderImplTest { - public JsonReaderImplTest() { if (!Charset.defaultCharset().equals(Charset.forName("UTF-8"))) { System.err.println("Default charset is " + Charset.defaultCharset() + ", must must be UTF-8"); @@ -63,6 +63,16 @@ public class JsonReaderImplTest { return Collections.EMPTY_MAP; } + @Test(expected = JsonParsingException.class) + public void badTypeObject() { + Json.createReaderFactory(getFactoryConfig()).createReader(new StringReader("[]")).readObject(); + } + + @Test(expected = JsonParsingException.class) + public void badTypeArray() { + Json.createReaderFactory(getFactoryConfig()).createReader(new StringReader("{}")).readArray(); + } + @Test public void simple() { final JsonReader reader = Json.createReaderFactory(getFactoryConfig()).createReader( @@ -173,7 +183,7 @@ public class JsonReaderImplTest { assertEquals("hallo\u20acö\uffff \u08a5 থ?ç$%&´'`*+#\udbff\udfff", object.getString("নa")); reader.close(); } - + @Test public void specialKeysWithStringAsByteArrayInputStream() { final String s = "{\"\\\"a\":\"\u0055\",\"\u0055\":\"test2\"}"; @@ -348,7 +358,7 @@ public class JsonReaderImplTest { assertEquals(-2, array.getInt(1)); reader.close(); } - + @Test public void simple2BadBufferSize8() { final JsonReader reader = Json.createReaderFactory(new HashMap<String, Object>() { @@ -435,8 +445,8 @@ public class JsonReaderImplTest { @Test public void stringescapeVariousBufferSizes() { - final int[] buffersizes = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 32, 64, 128, 1024, 8192 }; + final int[] buffersizes = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 32, 64, 128, 1024, 8192}; for (final int buffersize : buffersizes) { final String value = String.valueOf(buffersize); @@ -474,7 +484,7 @@ public class JsonReaderImplTest { public void testGrowingString() throws Throwable { JsonReaderFactory factory = Json.createReaderFactory(null); StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 40000; i++) { + for (int i = 0; i < 10000; i++) { sb.append('x'); String growingString = sb.toString(); String str = "[4, \"\", \"" + growingString + "\", \"\", \"" + growingString + "\", \"\", 400]"; @@ -507,7 +517,7 @@ public class JsonReaderImplTest { JsonReaderFactory factory = Json.createReaderFactory(config); StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 100; i++) { sb.append('x'); String name = sb.toString(); String str = "[4, \"\", \"" + name + "\", \"\", \"" + name + "\", \"\", 400]"; @@ -558,4 +568,12 @@ public class JsonReaderImplTest { assertEquals(1234.5, JsonNumber.class.cast(value).doubleValue(), 0.); } } + + + @Test(expected = JsonParsingException.class) + public void testInvalidNumber() { + String jsonWithIllegalNumber = "{\"val\":12.34-2}"; + JsonReaderImpl.class.cast(Json.createReader(new StringReader(jsonWithIllegalNumber))).readObject(); + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/java/org/apache/johnzon/core/OverflowTest.java ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/OverflowTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/OverflowTest.java new file mode 100644 index 0000000..a6879cf --- /dev/null +++ b/johnzon-core/src/test/java/org/apache/johnzon/core/OverflowTest.java @@ -0,0 +1,88 @@ +/* + * 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.johnzon.core; + +import org.junit.Assert; +import org.junit.Test; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObject; +import javax.json.JsonWriter; +import javax.json.JsonWriterFactory; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; + +public class OverflowTest { + @Test + public void ok() { + // normal content size + Json.createReaderFactory(new HashMap<String, Object>() {{ + put(JsonParserFactoryImpl.MAX_STRING_LENGTH, "10"); + put(JsonParserFactoryImpl.BUFFER_LENGTH, "2"); + }}).createReader(new StringReader("{\"a\":\"b\",\n\"another\":\"value\"}")).readObject(); + + // oversized + final JsonObject object = Json.createReaderFactory(new HashMap<String, Object>() {{ + put(JsonParserFactoryImpl.MAX_STRING_LENGTH, "10"); + put(JsonParserFactoryImpl.BUFFER_LENGTH, "2"); + }}).createReader(new StringReader("{\"a\":\"b\",\n\"another\":\"value very long\"}")).readObject(); + assertEquals("value very long", object.getString("another")); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void ko() { + Json.createReaderFactory(new HashMap<String, Object>() {{ + put(JsonParserFactoryImpl.MAX_STRING_LENGTH, "10"); + put(JsonParserFactoryImpl.BUFFER_LENGTH, "2"); + put(JsonParserFactoryImpl.AUTO_ADJUST_STRING_BUFFER, "false"); + }}).createReader(new StringReader("{\"another\":\"value too long\"}")).readObject(); + } + + @Test + public void testVeryLargeJson() { + JsonWriterFactory writerFactory = Json.createWriterFactory(new HashMap<String, Object>() {{ + put(JsonParserFactoryImpl.BUFFER_LENGTH, "256"); + }}); + + int itemSize = 50000; + + StringWriter sw = new StringWriter(); + JsonWriter writer = writerFactory.createWriter(sw); + + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + for (int i = 0; i < itemSize; i++) { + arrayBuilder.add("0123456789012345-" + i); + } + writer.writeArray(arrayBuilder.build()); + + String json = sw.toString(); + System.out.println("Created a JSON of size " + json.length() + " bytes"); + + // read it back in + JsonArray jsonArray = Json.createReader(new StringReader(json)).readArray(); + Assert.assertEquals(itemSize, jsonArray.size()); + + } +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-core/src/test/resources/json/jsonPointerTest.json ---------------------------------------------------------------------- diff --git a/johnzon-core/src/test/resources/json/jsonPointerTest.json b/johnzon-core/src/test/resources/json/jsonPointerTest.json new file mode 100644 index 0000000..8cdc1c9 --- /dev/null +++ b/johnzon-core/src/test/resources/json/jsonPointerTest.json @@ -0,0 +1,15 @@ +{ + "foo": [ + "bar", + "baz" + ], + "": 0, + "a/b": 1, + "c%d": 2, + "e^f": 3, + "g|h": 4, + "i\\j": 5, + "k\"l": 6, + " ": 7, + "m~n": 8 +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-distribution/pom.xml ---------------------------------------------------------------------- diff --git a/johnzon-distribution/pom.xml b/johnzon-distribution/pom.xml index 1240af0..ccb3d01 100644 --- a/johnzon-distribution/pom.xml +++ b/johnzon-distribution/pom.xml @@ -32,24 +32,23 @@ <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-json_1.0_spec</artifactId> - <version>${jsonspecversion}</version> + <version>${geronimo-jsonp.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-json_1.0_spec</artifactId> - <version>${jsonspecversion}</version> + <version>${geronimo-jsonp.version}</version> <scope>compile</scope> <classifier>sources</classifier> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-json_1.0_spec</artifactId> - <version>${jsonspecversion}</version> + <version>${geronimo-jsonp.version}</version> <scope>compile</scope> <classifier>javadoc</classifier> </dependency> - <dependency> <groupId>org.apache.johnzon</groupId> <artifactId>johnzon-core</artifactId> http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java index dd8b79c..e543804 100644 --- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java +++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java @@ -19,6 +19,7 @@ package org.apache.johnzon.jaxrs; import org.apache.johnzon.mapper.MapperBuilder; +import org.apache.johnzon.mapper.SerializeValueFilter; import org.apache.johnzon.mapper.access.AccessMode; import javax.json.JsonReaderFactory; @@ -34,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.util.Comparator; import java.util.List; @@ -116,6 +118,10 @@ public class ConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, Mes } } + public void setFailOnUnknownProperties(final boolean active) { + builder.setFailOnUnknownProperties(active); + } + public void setSupportConstructors(final boolean supportConstructors) { builder.setSupportConstructors(supportConstructors); } @@ -207,4 +213,33 @@ public class ConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, Mes public void setPrimitiveConverters(final boolean val) { builder.setPrimitiveConverters(val); } + + public MapperBuilder setDeduplicateObjects(boolean deduplicateObjects) { + return builder.setDeduplicateObjects(deduplicateObjects); + } + + public void setSerializeValueFilter(final String val) { + try { + builder.setSerializeValueFilter(SerializeValueFilter.class.cast( + Thread.currentThread().getContextClassLoader().loadClass(val).getConstructor().newInstance())); + } catch (final InstantiationException e) { + throw new IllegalArgumentException(e); + } catch (final IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (final NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getCause()); + } + } + + public void setUseBigDecimalForFloats(final boolean useBigDecimalForFloats) { + builder.setUseBigDecimalForFloats(useBigDecimalForFloats); + } + + public void setAutoAdjustStringBuffers(final boolean autoAdjustStringBuffers) { + builder.setAutoAdjustStringBuffers(autoAdjustStringBuffers); + } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java index f3d9a18..42fcbf0 100644 --- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java +++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java @@ -58,8 +58,10 @@ public class JohnzonMessageBodyReader<T> extends IgnorableTypes implements Messa public boolean isReadable(final Class<?> rawType, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return !isIgnored(rawType) - && InputStream.class != genericType && Reader.class != genericType && Response.class != genericType - && String.class != genericType + && !InputStream.class.isAssignableFrom(rawType) + && !Reader.class.isAssignableFrom(rawType) + && !Response.class.isAssignableFrom(rawType) + && !CharSequence.class.isAssignableFrom(rawType) && !JsonStructure.class.isAssignableFrom(rawType); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java index e6a8d36..6a3e163 100644 --- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java +++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java @@ -65,12 +65,12 @@ public class JohnzonMessageBodyWriter<T> extends IgnorableTypes implements Messa public boolean isWriteable(final Class<?> rawType, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return !isIgnored(rawType) - && InputStream.class != genericType - && OutputStream.class != genericType - && Writer.class != genericType - && StreamingOutput.class != genericType - && String.class != genericType - && Response.class != genericType + && !InputStream.class.isAssignableFrom(rawType) + && !OutputStream.class.isAssignableFrom(rawType) + && !Writer.class.isAssignableFrom(rawType) + && !StreamingOutput.class.isAssignableFrom(rawType) + && !CharSequence.class.isAssignableFrom(rawType) + && !Response.class.isAssignableFrom(rawType) && !JsonStructure.class.isAssignableFrom(rawType); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java index 4df4a7c..a895e07 100644 --- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java +++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java @@ -19,6 +19,7 @@ package org.apache.johnzon.jaxrs; import org.apache.johnzon.mapper.MapperBuilder; +import org.apache.johnzon.mapper.SerializeValueFilter; import org.apache.johnzon.mapper.access.AccessMode; import javax.json.JsonReaderFactory; @@ -34,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.util.Comparator; import java.util.List; @@ -43,14 +45,14 @@ import static java.util.Arrays.asList; @Provider @Produces({ - "*/json", - "*/*+json", "*/x-json", - "*/javascript", "*/x-javascript" + "*/json", + "*/*+json", "*/x-json", + "*/javascript", "*/x-javascript" }) @Consumes({ - "*/json", - "*/*+json", "*/x-json", - "*/javascript", "*/x-javascript" + "*/json", + "*/*+json", "*/x-json", + "*/javascript", "*/x-javascript" }) public class WildcardConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> { // build/configuration @@ -124,6 +126,10 @@ public class WildcardConfigurableJohnzonProvider<T> implements MessageBodyWriter } } + public void setFailOnUnknownProperties(final boolean active) { + builder.setFailOnUnknownProperties(active); + } + public void setSupportConstructors(final boolean supportConstructors) { builder.setSupportConstructors(supportConstructors); } @@ -215,4 +221,25 @@ public class WildcardConfigurableJohnzonProvider<T> implements MessageBodyWriter public void setPrimitiveConverters(final boolean val) { builder.setPrimitiveConverters(val); } + + public void setUseBigDecimalForFloats(final boolean useBigDecimalForFloats) { + builder.setUseBigDecimalForFloats(useBigDecimalForFloats); + } + + public void setSerializeValueFilter(final String val) { + try { + builder.setSerializeValueFilter(SerializeValueFilter.class.cast( + Thread.currentThread().getContextClassLoader().loadClass(val).getConstructor().newInstance())); + } catch (final InstantiationException e) { + throw new IllegalArgumentException(e); + } catch (final IllegalAccessException e) { + throw new IllegalArgumentException(e); + } catch (final NoSuchMethodException e) { + throw new IllegalArgumentException(e); + } catch (final ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getCause()); + } + } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jaxrs/src/test/java/org/apache/johnzon/jaxrs/JohnzonProviderTest.java ---------------------------------------------------------------------- diff --git a/johnzon-jaxrs/src/test/java/org/apache/johnzon/jaxrs/JohnzonProviderTest.java b/johnzon-jaxrs/src/test/java/org/apache/johnzon/jaxrs/JohnzonProviderTest.java index 6af6f68..a77752d 100644 --- a/johnzon-jaxrs/src/test/java/org/apache/johnzon/jaxrs/JohnzonProviderTest.java +++ b/johnzon-jaxrs/src/test/java/org/apache/johnzon/jaxrs/JohnzonProviderTest.java @@ -18,14 +18,20 @@ */ package org.apache.johnzon.jaxrs; -import org.apache.cxf.endpoint.Server; -import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; -import org.apache.cxf.jaxrs.client.WebClient; -import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; -import org.apache.cxf.transport.local.LocalConduit; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.GET; @@ -34,24 +40,25 @@ import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.client.Entity; +import javax.ws.rs.container.AsyncResponse; +import javax.ws.rs.container.Suspended; import javax.ws.rs.core.GenericType; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.StreamingOutput; -import java.io.IOException; -import java.io.OutputStream; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.apache.cxf.endpoint.Server; +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; +import org.apache.cxf.transport.local.LocalConduit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; public class JohnzonProviderTest { + private final static String ENDPOINT_ADDRESS = "local://johnzon"; + private static Server server; @BeforeClass @@ -72,7 +79,8 @@ public class JohnzonProviderTest { @Test public void asParam() { - final String result = client().path("johnzon").type(MediaType.APPLICATION_JSON_TYPE).post(new Johnzon("client")).readEntity(String.class); + final String result = client().path("johnzon").type(MediaType.APPLICATION_JSON_TYPE).post(new Johnzon("client")) + .readEntity(String.class); assertTrue(Boolean.parseBoolean(result)); } @@ -89,6 +97,19 @@ public class JohnzonProviderTest { } @Test + public void untypedStreamOutput() { + final StreamingOutput impl = new StreamingOutput() { + + @Override + public void write(final OutputStream outputStream) throws IOException, WebApplicationException { + // no-op + } + }; + assertFalse(new JohnzonMessageBodyWriter().isWriteable(impl.getClass(), impl.getClass(), new Annotation[0], + MediaType.APPLICATION_JSON_TYPE)); + } + + @Test public void primitive() { final String val = client(MediaType.TEXT_PLAIN_TYPE).path("johnzon/primitive").get(String.class); assertEquals("1986", val); @@ -106,9 +127,10 @@ public class JohnzonProviderTest { @Test public void list() { final ParameterizedType list = new ParameterizedType() { + @Override public Type[] getActualTypeArguments() { - return new Type[]{Johnzon.class}; + return new Type[] { Johnzon.class }; } @Override @@ -146,12 +168,14 @@ public class JohnzonProviderTest { } private static WebClient client(final MediaType mediaType) { - final WebClient client = WebClient.create(ENDPOINT_ADDRESS, singletonList(new JohnzonProvider<Object>())).accept(mediaType); + final WebClient client = WebClient.create(ENDPOINT_ADDRESS, singletonList(new JohnzonProvider<Object>())) + .accept(mediaType); WebClient.getConfig(client).getRequestContext().put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE); return client; } public static class Johnzon { + private String name; public Johnzon(final String name) { @@ -173,6 +197,7 @@ public class JohnzonProviderTest { @Path("johnzon") public static class JohnzonResource { + @GET public Johnzon johnzon() { return new Johnzon("johnzon"); @@ -207,6 +232,7 @@ public class JohnzonProviderTest { @Path("stream") public StreamingOutput out() { return new StreamingOutput() { + @Override public void write(OutputStream outputStream) throws IOException, WebApplicationException { outputStream.write("ok".getBytes()); @@ -215,6 +241,18 @@ public class JohnzonProviderTest { } @GET + @Path("lazy_stream") + public void out(@Suspended final AsyncResponse response) { + response.resume(new StreamingOutput() { + + @Override + public void write(OutputStream outputStream) throws IOException, WebApplicationException { + outputStream.write("ok".getBytes()); + } + }); + } + + @GET @Produces(MediaType.TEXT_PLAIN) @Path("primitive") public Integer primitive() { http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jsonb/pom.xml ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/pom.xml b/johnzon-jsonb/pom.xml index 66d49f6..9b52c33 100644 --- a/johnzon-jsonb/pom.xml +++ b/johnzon-jsonb/pom.xml @@ -27,6 +27,7 @@ <artifactId>johnzon-jsonb</artifactId> <name>Johnzon :: JSON-B Implementation</name> + <packaging>bundle</packaging> <properties> <java-compile.version>1.8</java-compile.version> @@ -35,24 +36,33 @@ <dependencies> <dependency> - <groupId>javax.ws.rs</groupId> - <artifactId>javax.ws.rs-api</artifactId> - <version>2.0</version> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-annotation_1.2_spec</artifactId> + <version>1.0</version> + <scope>provided</scope> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jaxrs_2.0_spec</artifactId> + <version>1.0-alpha-1</version> <scope>provided</scope> + <optional>true</optional> </dependency> <dependency> <groupId>org.apache.geronimo.specs</groupId> <artifactId>geronimo-jcdi_1.1_spec</artifactId> <version>1.0</version> <scope>provided</scope> + <optional>true</optional> </dependency> - <dependency> - <groupId>org.apache.johnzon</groupId> - <artifactId>jsonb-api</artifactId> - <version>1.0.1-SNAPSHOT</version> + <groupId>org.apache.geronimo.specs</groupId> + <artifactId>geronimo-jsonb_1.0_spec</artifactId> + <version>1.0</version> <scope>provided</scope> </dependency> + <dependency> <groupId>org.apache.johnzon</groupId> <artifactId>johnzon-mapper</artifactId> @@ -96,5 +106,19 @@ <scope>test</scope> </dependency> </dependencies> - <packaging>bundle</packaging> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Require-Capability>osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability> + <Provide-Capability>osgi.serviceloader;osgi.serviceloader=javax.json.bind.spi.JsonbProvider</Provide-Capability> + </instructions> + </configuration> + </plugin> + </plugins> + </build> </project> http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java index 189dce4..25cf998 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java @@ -36,20 +36,34 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; +import java.io.StringReader; import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.Collection; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Properties; +import java.util.function.Function; +import java.util.logging.Logger; + +import javax.annotation.Priority; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.ContextResolver; +import javax.ws.rs.ext.Providers; // here while we dont compile in java 8 jaxrs module, when migrated we'll merge it with IgnorableTypes hierarchy at least @Provider -@Produces("application/json") -@Consumes("application/json") -public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> { - private final Collection<String> ignores; - private final AtomicReference<Jsonb> delegate = new AtomicReference<>(); - private final JsonbConfig config = new JsonbConfig(); +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +@Priority(value = 4900) +public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T>, AutoCloseable { + + protected final Collection<String> ignores; + protected final JsonbConfig config = new JsonbConfig(); + protected volatile Function<Class<?>, Jsonb> delegate = null; + private boolean customized; + + @Context + private Providers providers; public JsonbJaxrsProvider() { this(null); @@ -59,41 +73,61 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR this.ignores = ignores; } - protected Jsonb createJsonb() { - return JsonbBuilder.create(config); - } - private boolean isIgnored(final Class<?> type) { return ignores != null && ignores.contains(type.getName()); } // config - main containers support the configuration of providers this way + public void setFailOnUnknownProperties(final boolean active) { + config.setProperty("johnzon.fail-on-unknown-properties", active); + customized = true; + } + + public void setOtherProperties(final String others) { + final Properties properties = new Properties() {{ + try { + load(new StringReader(others)); + } catch (final IOException e) { + throw new IllegalArgumentException(e); + } + }}; + properties.stringPropertyNames().forEach(k -> config.setProperty(k, properties.getProperty(k))); + customized = true; + } + public void setIJson(final boolean active) { config.withStrictIJSON(active); + customized = true; } public void setEncoding(final String encoding) { config.withEncoding(encoding); + customized = true; } public void setBinaryDataStrategy(final String binaryDataStrategy) { config.withBinaryDataStrategy(binaryDataStrategy); + customized = true; } public void setPropertyNamingStrategy(final String propertyNamingStrategy) { config.withPropertyNamingStrategy(propertyNamingStrategy); + customized = true; } public void setPropertyOrderStrategy(final String propertyOrderStrategy) { config.withPropertyOrderStrategy(propertyOrderStrategy); + customized = true; } public void setNullValues(final boolean nulls) { config.withNullValues(nulls); + customized = true; } public void setPretty(final boolean pretty) { config.withFormatting(pretty); + customized = true; } // actual impl @@ -101,21 +135,23 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR @Override public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return !isIgnored(type) - && InputStream.class != genericType && Reader.class != genericType && Response.class != genericType - && String.class != genericType - && !JsonStructure.class.isAssignableFrom(type); + && !InputStream.class.isAssignableFrom(type) + && !Reader.class.isAssignableFrom(type) + && !Response.class.isAssignableFrom(type) + && !CharSequence.class.isAssignableFrom(type) + && !JsonStructure.class.isAssignableFrom(type); } @Override public boolean isWriteable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) { return !isIgnored(type) - && InputStream.class != genericType - && OutputStream.class != genericType - && Writer.class != genericType - && StreamingOutput.class != genericType - && String.class != genericType - && Response.class != genericType - && !JsonStructure.class.isAssignableFrom(type); + && !InputStream.class.isAssignableFrom(type) + && !OutputStream.class.isAssignableFrom(type) + && !Writer.class.isAssignableFrom(type) + && !StreamingOutput.class.isAssignableFrom(type) + && !CharSequence.class.isAssignableFrom(type) + && !Response.class.isAssignableFrom(type) + && !JsonStructure.class.isAssignableFrom(type); } @Override @@ -125,26 +161,75 @@ public class JsonbJaxrsProvider<T> implements MessageBodyWriter<T>, MessageBodyR @Override public T readFrom(final Class<T> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { - return delegate().fromJson(entityStream, genericType); + final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException { + return getJsonb(type).fromJson(entityStream, genericType); } @Override public void writeTo(final T t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType, - final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { - delegate().toJson(t, entityStream); - } - - private Jsonb delegate() { - Jsonb jsonb = delegate.get(); - if (jsonb == null) { - final Jsonb newJsonb = createJsonb(); - if (delegate.compareAndSet(null, newJsonb)) { - jsonb = newJsonb; - } else { - jsonb = delegate.get(); + final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException { + getJsonb(type).toJson(t, entityStream); + } + + protected Jsonb createJsonb() { + return JsonbBuilder.create(config); + } + + protected Jsonb getJsonb(final Class<?> type) { + if (delegate == null){ + synchronized (this) { + if (delegate == null) { + final ContextResolver<Jsonb> contextResolver = providers.getContextResolver(Jsonb.class, MediaType.APPLICATION_JSON_TYPE); + if (contextResolver != null) { + if (customized) { + Logger.getLogger(JsonbJaxrsProvider.class.getName()) + .warning("Customizations done on the Jsonb instance will be ignored because a ContextResolver<Jsonb> was found"); + } + delegate = new DynamicInstance(contextResolver); // faster than contextResolver::getContext + } else { + delegate = new ProvidedInstance(createJsonb()); // don't recreate it + } + } } } - return jsonb; + return delegate.apply(type); + } + + @Override + public synchronized void close() throws Exception { + if (AutoCloseable.class.isInstance(delegate)) { + AutoCloseable.class.cast(delegate).close(); + } + } + + private static final class DynamicInstance implements Function<Class<?>, Jsonb> { + private final ContextResolver<Jsonb> contextResolver; + + private DynamicInstance(final ContextResolver<Jsonb> resolver) { + this.contextResolver = resolver; + } + + @Override + public Jsonb apply(final Class<?> type) { + return contextResolver.getContext(type); + } + } + + private static final class ProvidedInstance implements Function<Class<?>, Jsonb>, AutoCloseable { + private final Jsonb instance; + + private ProvidedInstance(final Jsonb instance) { + this.instance = instance; + } + + @Override + public Jsonb apply(final Class<?> aClass) { + return instance; + } + + @Override + public void close() throws Exception { + instance.close(); + } } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java deleted file mode 100644 index 7308374..0000000 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnsonJsonb.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * 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.johnzon.jsonb; - -import org.apache.johnzon.mapper.Mapper; -import org.apache.johnzon.mapper.MapperException; -import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType; - -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.Writer; -import java.lang.reflect.Array; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Collection; -import java.util.Optional; -import java.util.OptionalDouble; -import java.util.OptionalInt; -import java.util.OptionalLong; - -// TODO: Optional handling for lists (and arrays)? -public class JohnsonJsonb implements Jsonb, AutoCloseable { - private final Mapper delegate; - - public JohnsonJsonb(final Mapper build) { - this.delegate = build; - } - - @Override - public <T> T fromJson(final String str, final Class<T> type) throws JsonbException { - try { - if (isArray(type)) { - return delegate.readTypedArray(new StringReader(str), type.getComponentType(), type); - } else if (Collection.class.isAssignableFrom(type)) { - return (T) delegate.readCollection(new StringReader(str), new JohnzonParameterizedType(type, Object.class)); - } - final Type mappingType = unwrapPrimitiveOptional(type); - final Object object = delegate.readObject(str, mappingType); - if (mappingType != type) { - return wrapPrimitiveOptional(object, type); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - private <T> T wrapPrimitiveOptional(final Object object, final Type type) { - if (OptionalDouble.class == type) { - if (object == null) { - return (T) OptionalDouble.empty(); - } - return (T) OptionalDouble.of(Number.class.cast(object).doubleValue()); - } else if (OptionalInt.class == type) { - if (object == null) { - return (T) OptionalInt.empty(); - } - return (T) OptionalInt.of(Number.class.cast(object).intValue()); - } else if (OptionalLong.class == type) { - if (object == null) { - return (T) OptionalLong.empty(); - } - return (T) OptionalLong.of(Number.class.cast(object).longValue()); - } - // Optional - return (T) Optional.ofNullable(object); - } - - private Type unwrapPrimitiveOptional(final Type type) { - if (OptionalDouble.class == type) { - return double.class; - } else if (OptionalInt.class == type) { - return int.class; - } else if (OptionalLong.class == type) { - return long.class; - } else if (ParameterizedType.class.isInstance(type)) { - final ParameterizedType pt = ParameterizedType.class.cast(type); - if (Optional.class == pt.getRawType()) { - return pt.getActualTypeArguments()[0]; - } - } - return type; - } - - @Override - public <T> T fromJson(final String str, final Type runtimeType) throws JsonbException { - try { - if (isArray(runtimeType)) { - final Class cast = Class.class.cast(runtimeType); - return (T) delegate.readTypedArray(new StringReader(str), cast.getComponentType(), cast); - } else if (isCollection(runtimeType)) { - return (T) delegate.readCollection(new StringReader(str), ParameterizedType.class.cast(runtimeType)); - } - final Type mappingType = unwrapPrimitiveOptional(runtimeType); - final Object object = delegate.readObject(str, mappingType); - if (mappingType != runtimeType) { - return wrapPrimitiveOptional(object, runtimeType); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - @Override - public <T> T fromJson(final Reader reader, final Class<T> type) throws JsonbException { - try { - if (isArray(type)) { - return delegate.readTypedArray(reader, type.getComponentType(), type); - } else if (Collection.class.isAssignableFrom(type)) { - return (T) delegate.readCollection(reader, new JohnzonParameterizedType(type, Object.class)); - } - final Type mappingType = unwrapPrimitiveOptional(type); - final Object object = delegate.readObject(reader, mappingType); - if (mappingType != type) { - return wrapPrimitiveOptional(object, type); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - @Override - public <T> T fromJson(final Reader reader, final Type runtimeType) throws JsonbException { - try { - if (isArray(runtimeType)) { - final Class<T> type = Class.class.cast(runtimeType); - return delegate.readTypedArray(reader, type.getComponentType(), type); - } else if (isCollection(runtimeType)) { - return (T) delegate.readCollection(reader, ParameterizedType.class.cast(runtimeType)); - } - final Type mappingType = unwrapPrimitiveOptional(runtimeType); - final Object object = delegate.readObject(reader, mappingType); - if (mappingType != runtimeType) { - return wrapPrimitiveOptional(object, runtimeType); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - @Override - public <T> T fromJson(final InputStream stream, final Class<T> type) throws JsonbException { - try { - if (isArray(type)) { - return delegate.readTypedArray(stream, type.getComponentType(), type); - } else if (Collection.class.isAssignableFrom(type)) { - return (T) delegate.readCollection(stream, new JohnzonParameterizedType(type, Object.class)); - } - final Type mappingType = unwrapPrimitiveOptional(type); - final Object object = delegate.readObject(stream, mappingType); - if (mappingType != type) { - return wrapPrimitiveOptional(object, type); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - @Override - public <T> T fromJson(final InputStream stream, final Type runtimeType) throws JsonbException { - try { - if (isArray(runtimeType)) { - final Class<T> type = Class.class.cast(runtimeType); - return delegate.readTypedArray(stream, type.getComponentType(), type); - } else if (isCollection(runtimeType)) { - return (T) delegate.readCollection(stream, ParameterizedType.class.cast(runtimeType)); - } - - final Type mappingType = unwrapPrimitiveOptional(runtimeType); - final Object object = delegate.readObject(stream, mappingType); - if (mappingType != runtimeType) { - return wrapPrimitiveOptional(object, runtimeType); - } - return (T) object; - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - @Override - public String toJson(final Object inObject) throws JsonbException { - try { - final Object object = unwrapOptional(inObject); - if (object == null) { - return "null"; - } - if (isArray(object.getClass())) { - return delegate.writeArrayAsString(toArray(object)); - } else if (Collection.class.isInstance(object)) { - return delegate.writeArrayAsString(Collection.class.cast(object)); - } - return delegate.writeObjectAsString(object); - - } catch (final MapperException me) { - throw new JsonbException(me.getMessage(), me); - } - } - - private Object[] toArray(final Object object) { - final Class<?> componentType = object.getClass().getComponentType(); - Object[] array; - if (int.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getInt(object, i); - } - } else if (double.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getDouble(object, i); - } - } else if (byte.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getByte(object, i); - } - } else if (char.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getChar(object, i); - } - } else if (float.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getFloat(object, i); - } - } else if (long.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getLong(object, i); - } - } else if (short.class == componentType) { - final int length = Array.getLength(object); - array = new Integer[length]; - for (int i = 0; i < length; i++) { - array[i] = Array.getShort(object, i); - } - } else { - array = (Object[]) object; - } - return array; - } - - @Override - public String toJson(final Object inObject, final Type runtimeType) throws JsonbException { - final Object object = unwrapOptional(inObject); - if (object != null && isArray(runtimeType)) { - return delegate.writeArrayAsString((Object[]) object); - } else if (isCollection(runtimeType)) { - return delegate.writeArrayAsString(Collection.class.cast(object)); - } - return delegate.writeObjectAsString(object); - } - - @Override - public void toJson(final Object inObject, final Writer writer) throws JsonbException { - final Object object = unwrapOptional(inObject); - if (object != null && isArray(object.getClass())) { - delegate.writeArray((Object[]) object, writer); - } else if (Collection.class.isInstance(object)) { - delegate.writeArray(Collection.class.cast(object), writer); - } else { - delegate.writeObject(object, writer); - } - } - - @Override - public void toJson(final Object inObject, final Type runtimeType, final Writer writer) throws JsonbException { - final Object object = unwrapOptional(inObject); - if (object != null && isArray(runtimeType)) { - delegate.writeArray((Object[]) object, writer); - } else if (isCollection(runtimeType)) { - delegate.writeArray(Collection.class.cast(object), writer); - } else { - delegate.writeObject(object, writer); - } - } - - @Override - public void toJson(final Object inObject, final OutputStream stream) throws JsonbException { - final Object object = unwrapOptional(inObject); - if (object != null && isArray(object.getClass())) { - delegate.writeArray((Object[]) object, stream); - } else if (Collection.class.isInstance(object)) { - delegate.writeArray(Collection.class.cast(object), stream); - } else { - delegate.writeObject(object, stream); - } - } - - @Override - public void toJson(final Object inObject, final Type runtimeType, final OutputStream stream) throws JsonbException { - final Object object = unwrapOptional(inObject); - if (object != null && isArray(runtimeType)) { - delegate.writeArray((Object[]) object, stream); - } else if (isCollection(runtimeType)) { - delegate.writeArray(Collection.class.cast(object), stream); - } else { - delegate.writeObject(object, stream); - } - } - - private Object unwrapOptional(final Object inObject) { - if (Optional.class.isInstance(inObject)) { - return Optional.class.cast(inObject).orElse(null); - } - if (OptionalInt.class.isInstance(inObject)) { - final OptionalInt optionalInt = OptionalInt.class.cast(inObject); - return optionalInt.isPresent() ? optionalInt.getAsInt() : null; - } - if (OptionalLong.class.isInstance(inObject)) { - final OptionalLong optionalLong = OptionalLong.class.cast(inObject); - return optionalLong.isPresent() ? optionalLong.getAsLong() : null; - } - if (OptionalDouble.class.isInstance(inObject)) { - final OptionalDouble optionalDouble = OptionalDouble.class.cast(inObject); - return optionalDouble.isPresent() ? optionalDouble.getAsDouble() : null; - } - return inObject; - } - - private boolean isArray(final Type runtimeType) { - return Class.class.isInstance(runtimeType) && Class.class.cast(runtimeType).isArray(); - } - - private boolean isCollection(final Type runtimeType) { - if (!ParameterizedType.class.isInstance(runtimeType)) { - return false; - } - final Type rawType = ParameterizedType.class.cast(runtimeType).getRawType(); - return Class.class.isInstance(rawType) && Collection.class.isAssignableFrom(Class.class.cast(rawType)); - } - - @Override - public void close() { - delegate.close(); - } -} http://git-wip-us.apache.org/repos/asf/johnzon/blob/76fe13de/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java ---------------------------------------------------------------------- diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java index 137cf4c..40ec155 100644 --- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java +++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java @@ -20,6 +20,7 @@ package org.apache.johnzon.jsonb; import org.apache.johnzon.core.AbstractJsonFactory; import org.apache.johnzon.core.JsonGeneratorFactoryImpl; +import org.apache.johnzon.core.JsonParserFactoryImpl; import org.apache.johnzon.jsonb.cdi.CDIs; import org.apache.johnzon.jsonb.converter.JohnzonJsonbAdapter; import org.apache.johnzon.jsonb.factory.SimpleJohnzonAdapterFactory; @@ -31,6 +32,9 @@ import org.apache.johnzon.mapper.Converter; import org.apache.johnzon.mapper.Mapper; import org.apache.johnzon.mapper.MapperBuilder; import org.apache.johnzon.mapper.ObjectConverter; +import org.apache.johnzon.mapper.SerializeValueFilter; +import org.apache.johnzon.mapper.access.AccessMode; +import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode; import org.apache.johnzon.mapper.internal.AdapterKey; import org.apache.johnzon.mapper.internal.ConverterAdapter; @@ -48,7 +52,9 @@ import javax.json.bind.serializer.JsonbSerializer; import javax.json.spi.JsonProvider; import javax.json.stream.JsonGenerator; import javax.json.stream.JsonParserFactory; +import java.io.Closeable; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; @@ -84,8 +90,19 @@ import java.util.function.Supplier; import java.util.stream.Stream; import static java.time.format.DateTimeFormatter.ofPattern; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.MILLI_OF_SECOND; +import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoField.YEAR; +import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalQueries; import static java.util.Collections.emptyMap; +import java.util.Objects; import static java.util.Optional.ofNullable; +import java.util.concurrent.TimeUnit; import static javax.json.bind.config.PropertyNamingStrategy.IDENTITY; import static javax.json.bind.config.PropertyOrderStrategy.LEXICOGRAPHICAL; @@ -114,27 +131,9 @@ public class JohnzonBuilder implements JsonbBuilder { public Jsonb build() { if (jsonp != null) { builder.setGeneratorFactory(jsonp.createGeneratorFactory(generatorConfig())); - builder.setReaderFactory(jsonp.createReaderFactory(emptyMap())); + builder.setReaderFactory(jsonp.createReaderFactory(readerConfig())); } - final Supplier<JsonParserFactory> parserFactoryProvider = new Supplier<JsonParserFactory>() { // thread safety is not mandatory - private final AtomicReference<JsonParserFactory> ref = new AtomicReference<>(); - - @Override - public JsonParserFactory get() { - JsonParserFactory factory = ref.get(); - if (factory == null) { - factory = doCreate(); - if (!ref.compareAndSet(null, factory)) { - factory = ref.get(); - } - } - return factory; - } - - private JsonParserFactory doCreate() { - return (jsonp == null ? JsonProvider.provider() : jsonp).createParserFactory(emptyMap()); - } - }; + final Supplier<JsonParserFactory> parserFactoryProvider = createJsonParserFactory(); if (config == null) { config = new JsonbConfig(); @@ -207,6 +206,12 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty("johnzon.primitiveConverters") .map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v)) .ifPresent(builder::setPrimitiveConverters); + config.getProperty("johnzon.useBigDecimalForFloats") + .map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v)) + .ifPresent(builder::setUseBigDecimalForFloats); + config.getProperty("johnzon.deduplicateObjects") + .map(v -> !Boolean.class.isInstance(v) ? Boolean.parseBoolean(v.toString()) : Boolean.class.cast(v)) + .ifPresent(builder::setDeduplicateObjects); final Map<AdapterKey, Adapter<?, ?>> defaultConverters = createJava8Converters(builder); @@ -230,11 +235,16 @@ public class JohnzonBuilder implements JsonbBuilder { } throw new IllegalArgumentException("Unsupported factory: " + val); }).orElseGet(this::findFactory); - final JsonbAccessMode accessMode = new JsonbAccessMode( - propertyNamingStrategy, orderValue, visibilityStrategy, - !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE), - defaultConverters, - factory, parserFactoryProvider); + final AccessMode accessMode = config.getProperty("johnzon.accessMode") + .map(this::toAccessMode) + .orElseGet(() -> new JsonbAccessMode( + propertyNamingStrategy, orderValue, visibilityStrategy, + !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE), + defaultConverters, + factory, parserFactoryProvider, + config.getProperty("johnzon.accessModeDelegate") + .map(this::toAccessMode) + .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false)))); builder.setAccessMode(accessMode); @@ -253,6 +263,9 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty(JsonbConfig.STRICT_IJSON).map(Boolean.class::cast).ifPresent(ijson -> { // no-op: https://tools.ietf.org/html/rfc7493 the only MUST of the spec should be fine by default }); + config.getProperty("johnzon.fail-on-unknown-properties") + .map(v -> Boolean.class.isInstance(v) ? Boolean.class.cast(v) : Boolean.parseBoolean(String.valueOf(v))) + .ifPresent(builder::setFailOnUnknownProperties); config.getProperty(JsonbConfig.BINARY_DATA_STRATEGY).map(String.class::cast).ifPresent(bin -> { switch (bin) { @@ -284,6 +297,23 @@ public class JohnzonBuilder implements JsonbBuilder { builder.setReadAttributeBeforeWrite( config.getProperty("johnzon.readAttributeBeforeWrite").map(Boolean.class::cast).orElse(false)); + builder.setAutoAdjustStringBuffers( + config.getProperty("johnzon.autoAdjustBuffer").map(Boolean.class::cast).orElse(true)); + config.getProperty("johnzon.serialize-value-filter") + .map(s -> { + if (String.class.isInstance(s)) { + try { + return SerializeValueFilter.class.cast( + Thread.currentThread().getContextClassLoader().loadClass(s.toString()).getConstructor().newInstance()); + } catch (final InstantiationException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getCause()); + } + } + return s; + }) + .ifPresent(s -> builder.setSerializeValueFilter(SerializeValueFilter.class.cast(s))); config.getProperty(JsonbConfig.SERIALIZERS).map(JsonbSerializer[].class::cast).ifPresent(serializers -> { Stream.of(serializers).forEach(s -> { @@ -298,7 +328,7 @@ public class JohnzonBuilder implements JsonbBuilder { } builder.addObjectConverter( Class.class.cast(args[0]), (ObjectConverter.Writer) - (instance, jsonbGenerator) -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator))); + (instance, jsonbGenerator) -> s.serialize(instance, jsonbGenerator.getJsonGenerator(), new JohnzonSerializationContext(jsonbGenerator))); }); }); config.getProperty(JsonbConfig.DESERIALIZERS).map(JsonbDeserializer[].class::cast).ifPresent(deserializers -> { @@ -314,15 +344,18 @@ public class JohnzonBuilder implements JsonbBuilder { // TODO: support PT in ObjectConverter (list) builder.addObjectConverter( Class.class.cast(args[0]), (ObjectConverter.Reader) - (jsonObject, targetType, parser) -> d.deserialize( - parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); + (jsonObject, targetType, parser) -> d.deserialize( + parserFactoryProvider.get().createParser(jsonObject), new JohnzonDeserializationContext(parser), targetType)); }); }); final boolean useCdi = cdiIntegration != null && cdiIntegration.isCanWrite() && config.getProperty("johnzon.cdi.activated").map(Boolean.class::cast).orElse(Boolean.TRUE); - final Mapper mapper = builder.addCloseable(accessMode).build(); + if (Closeable.class.isInstance(accessMode)) { + builder.addCloseable(Closeable.class.cast(accessMode)); + } + final Mapper mapper = builder.build(); - return useCdi ? new JohnsonJsonb(mapper) { + return useCdi ? new JohnzonJsonb(mapper) { { cdiIntegration.track(this); } @@ -337,14 +370,50 @@ public class JohnzonBuilder implements JsonbBuilder { } } } - } : new JohnsonJsonb(mapper); + } : new JohnzonJsonb(mapper); + } + + private AccessMode toAccessMode(final Object s) { + if (String.class.isInstance(s)) { + try { + return AccessMode.class.cast( + Thread.currentThread().getContextClassLoader().loadClass(s.toString()).getConstructor().newInstance()); + } catch (final InstantiationException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } catch (InvocationTargetException e) { + throw new IllegalArgumentException(e.getCause()); + } + } + return AccessMode.class.cast(s); + } + + private Supplier<JsonParserFactory> createJsonParserFactory() { + return new Supplier<JsonParserFactory>() { // thread safety is not mandatory + private final AtomicReference<JsonParserFactory> ref = new AtomicReference<>(); + + @Override + public JsonParserFactory get() { + JsonParserFactory factory = ref.get(); + if (factory == null) { + factory = doCreate(); + if (!ref.compareAndSet(null, factory)) { + factory = ref.get(); + } + } + return factory; + } + + private JsonParserFactory doCreate() { + return (jsonp == null ? JsonProvider.provider() : jsonp).createParserFactory(emptyMap()); + } + }; } private ParameterizedType findPT(final Object s, final Class<?> type) { return ParameterizedType.class.cast( - Stream.of(s.getClass().getGenericInterfaces()) - .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == type) - .findFirst().orElse(null)); + Stream.of(s.getClass().getGenericInterfaces()) + .filter(i -> ParameterizedType.class.isInstance(i) && ParameterizedType.class.cast(i).getRawType() == type) + .findFirst().orElse(null)); } private Object getBeanManager() { @@ -572,98 +641,151 @@ public class JohnzonBuilder implements JsonbBuilder { final Optional<Locale> locale = config.getProperty(JsonbConfig.LOCALE).map(Locale.class::cast); final DateTimeFormatter formatter = locale.isPresent() ? ofPattern(dateFormat, locale.get()) : ofPattern(dateFormat); - // Note: we try and fallback in the parsing cause we don't know if the date format provided is - // for date, datetime, time - converters.put(new AdapterKey(Date.class, String.class), new ConverterAdapter<>(new Converter<Date>() { - private volatile boolean useFormatter = true; @Override public String toString(final Date instance) { - return LocalDateTime.ofInstant(instance.toInstant(), zoneIDUTC).toString(); + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); } @Override public Date fromString(final String text) { - if (useFormatter) { - try { - return Date.from(LocalDateTime.parse(text, formatter).toInstant(ZoneOffset.UTC)); - } catch (final DateTimeParseException dpe) { - useFormatter = false; - } + try { + return Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant()); + } catch (final DateTimeParseException dpe) { + return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC)); } - return Date.from(LocalDateTime.parse(text).toInstant(ZoneOffset.UTC)); } })); converters.put(new AdapterKey(LocalDateTime.class, String.class), new ConverterAdapter<>(new Converter<LocalDateTime>() { - private volatile boolean useFormatter = true; @Override public String toString(final LocalDateTime instance) { - return instance.toString(); + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(ZoneOffset.UTC), zoneIDUTC)); } @Override public LocalDateTime fromString(final String text) { - if (useFormatter) { - try { - return LocalDateTime.parse(text, formatter); - } catch (final DateTimeParseException dpe) { - useFormatter = false; - } + try { + return parseZonedDateTime(text, formatter, zoneIDUTC).toLocalDateTime(); + } catch (final DateTimeParseException dpe) { + return LocalDateTime.parse(text); } - return LocalDateTime.parse(text); } })); converters.put(new AdapterKey(LocalDate.class, String.class), new ConverterAdapter<>(new Converter<LocalDate>() { - private volatile boolean useFormatter = true; @Override public String toString(final LocalDate instance) { - return instance.toString(); + return formatter.format(ZonedDateTime.ofInstant(Instant.ofEpochMilli(TimeUnit.DAYS.toMillis(instance.toEpochDay())), zoneIDUTC)); } @Override public LocalDate fromString(final String text) { - if (useFormatter) { - try { - return LocalDate.parse(text, formatter); - } catch (final DateTimeParseException dpe) { - useFormatter = false; - } + try { + return parseZonedDateTime(text, formatter, zoneIDUTC).toLocalDate(); + } catch (final DateTimeParseException dpe) { + return LocalDate.parse(text); + } + } + })); + converters.put(new AdapterKey(OffsetDateTime.class, String.class), new ConverterAdapter<>(new Converter<OffsetDateTime>() { + + @Override + public String toString(final OffsetDateTime instance) { + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); + } + + @Override + public OffsetDateTime fromString(final String text) { + try { + return parseZonedDateTime(text, formatter, zoneIDUTC).toOffsetDateTime(); + } catch (final DateTimeParseException dpe) { + return OffsetDateTime.parse(text); } - return LocalDate.parse(text); } })); converters.put(new AdapterKey(ZonedDateTime.class, String.class), new ConverterAdapter<>(new Converter<ZonedDateTime>() { - private volatile boolean useFormatter = true; @Override public String toString(final ZonedDateTime instance) { - return instance.toString(); + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); } @Override public ZonedDateTime fromString(final String text) { - if (useFormatter) { - try { - return ZonedDateTime.parse(text, formatter); - } catch (final DateTimeParseException dpe) { - useFormatter = false; - } + try { + return parseZonedDateTime(text, formatter, zoneIDUTC); + } catch (final DateTimeParseException dpe) { + return ZonedDateTime.parse(text); } - return ZonedDateTime.parse(text); + } + })); + converters.put(new AdapterKey(Calendar.class, String.class), new ConverterAdapter<>(new Converter<Calendar>() { + + @Override + public String toString(final Calendar instance) { + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); + } + + @Override + public Calendar fromString(final String text) { + Calendar instance = Calendar.getInstance(); + instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant())); + return instance; + } + })); + converters.put(new AdapterKey(GregorianCalendar.class, String.class), new ConverterAdapter<>(new Converter<GregorianCalendar>() { + + @Override + public String toString(final GregorianCalendar instance) { + return formatter.format(ZonedDateTime.ofInstant(instance.toInstant(), zoneIDUTC)); + } + + @Override + public GregorianCalendar fromString(final String text) { + Calendar instance = GregorianCalendar.getInstance(); + instance.setTime(Date.from(parseZonedDateTime(text, formatter, zoneIDUTC).toInstant())); + return GregorianCalendar.class.cast(instance); + } + })); + converters.put(new AdapterKey(Instant.class, String.class), new ConverterAdapter<>(new Converter<Instant>() { + + @Override + public String toString(final Instant instance) { + return formatter.format(ZonedDateTime.ofInstant(instance, zoneIDUTC)); + } + + @Override + public Instant fromString(final String text) { + return parseZonedDateTime(text, formatter, zoneIDUTC).toInstant(); } })); }); } + + private static ZonedDateTime parseZonedDateTime(final String text, final DateTimeFormatter formatter, final ZoneId defaultZone){ + TemporalAccessor parse = formatter.parse(text); + ZoneId zone = parse.query(TemporalQueries.zone()); + if (Objects.isNull(zone)) { + zone = defaultZone; + } + int year = parse.isSupported(YEAR) ? parse.get(YEAR) : 0; + int month = parse.isSupported(MONTH_OF_YEAR) ? parse.get(MONTH_OF_YEAR) : 0; + int day = parse.isSupported(DAY_OF_MONTH) ? parse.get(DAY_OF_MONTH) : 0; + int hour = parse.isSupported(HOUR_OF_DAY) ? parse.get(HOUR_OF_DAY) : 0; + int minute = parse.isSupported(MINUTE_OF_HOUR) ? parse.get(MINUTE_OF_HOUR) : 0; + int second = parse.isSupported(SECOND_OF_MINUTE) ? parse.get(SECOND_OF_MINUTE) : 0; + int millisecond = parse.isSupported(MILLI_OF_SECOND) ? parse.get(MILLI_OF_SECOND) : 0; + return ZonedDateTime.of(year, month, day, hour, minute, second, millisecond, zone); + } private static void logIfDeprecatedTimeZone(final String text) { /* TODO: get the list, UTC is clearly not deprecated but uses 3 letters if (text.length() == 3) { // don't fail but log it Logger.getLogger(JohnzonBuilder.class.getName()).severe("Deprecated timezone: " + text); } - */ + */ } private Map<String, ?> generatorConfig() { @@ -676,4 +798,16 @@ public class JohnzonBuilder implements JsonbBuilder { config.getProperty(JsonbConfig.FORMATTING).ifPresent(b -> map.put(JsonGenerator.PRETTY_PRINTING, b)); return map; } + + private Map<String, ?> readerConfig() { + final Map<String, Object> map = new HashMap<>(); + if (config == null) { + return map; + } + config.getProperty(JsonParserFactoryImpl.BUFFER_LENGTH).ifPresent(b -> map.put(JsonParserFactoryImpl.BUFFER_LENGTH, b)); + config.getProperty(JsonParserFactoryImpl.MAX_STRING_LENGTH).ifPresent(b -> map.put(JsonParserFactoryImpl.MAX_STRING_LENGTH, b)); + config.getProperty(JsonParserFactoryImpl.SUPPORTS_COMMENTS).ifPresent(b -> map.put(JsonParserFactoryImpl.SUPPORTS_COMMENTS, b)); + config.getProperty(AbstractJsonFactory.BUFFER_STRATEGY).ifPresent(b -> map.put(AbstractJsonFactory.BUFFER_STRATEGY, b)); + return map; + } }
