diff --git a/.travis.yml b/.travis.yml index 8cdde67be..8440824bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,10 @@ install: true jobs: include: + - stage: checkstyle + script: mvn checkstyle:checkstyle + - stage: copyright + script: bash etc/copyright.sh - stage: install-yasson script: mvn -U -C -Pstaging clean install # - stage: run-jmh @@ -31,11 +35,7 @@ jobs: # - cd yasson-jmh # - mvn clean install # - java -jar target/yasson-jmh.jar -t 1 -f 2 - - stage: checkstyle - script: mvn checkstyle:checkstyle - - stage: copyright - script: mvn glassfish-copyright:check - stage: tck-run - script: bash tck.sh + script: bash etc/tck.sh diff --git a/etc/copyright.sh b/etc/copyright.sh new file mode 100644 index 000000000..e97c07232 --- /dev/null +++ b/etc/copyright.sh @@ -0,0 +1,22 @@ +#!/bin/bash -x +# +# Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, +# or the Eclipse Distribution License v. 1.0 which is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +# + +die(){ echo "${1}" ; exit 1 ;} + +readonly RESULT_FILE="copyright-check.txt" + +mvn -q org.glassfish.copyright:glassfish-copyright-maven-plugin:copyright \ + > ${RESULT_FILE} || (cat ${RESULT_FILE}; die "Error running the Maven command") + +grep -i "copyright" ${RESULT_FILE} \ + && die "COPYRIGHT ERROR" || echo "COPYRIGHT OK" \ No newline at end of file diff --git a/tck.sh b/etc/tck.sh old mode 100755 new mode 100644 similarity index 94% rename from tck.sh rename to etc/tck.sh index 5e35de2bf..0be320f5e --- a/tck.sh +++ b/etc/tck.sh @@ -17,7 +17,8 @@ GF_BUNDLE_URL="central.maven.org/maven2/org/glassfish/main/distributions/glassfi TCK_NAME=jsonb-tck TCK_VERSION=1.0.1 -export TCK_HOME=`pwd`"/target-tck" +export YASSON_HOME=`pwd` +export TCK_HOME=${YASSON_HOME}"/target-tck" rm -r ${TCK_HOME} mkdir ${TCK_HOME} cd ${TCK_HOME} @@ -33,7 +34,7 @@ wget -q --no-cache ${GF_BUNDLE_URL} -O latest-glassfish.zip echo "Exporting downloaded GlassFish" unzip -qq ${TCK_HOME}/latest-glassfish.zip -d ${TCK_HOME} -cp -a ${TCK_HOME}/target/yasson.jar ${TCK_HOME}/glassfish5/glassfish/modules/yasson.jar +cp -a ${YASSON_HOME}/target/yasson.jar ${TCK_HOME}/glassfish5/glassfish/modules/yasson.jar cd ${TS_HOME}/bin diff --git a/pom.xml b/pom.xml index c9102017e..9a9187176 100644 --- a/pom.xml +++ b/pom.xml @@ -201,8 +201,18 @@ org.glassfish.copyright glassfish-copyright-maven-plugin + - run-copyright + print-copyright + + copyright + + validate + + + + check-copyright check @@ -510,7 +520,7 @@ org.glassfish.copyright glassfish-copyright-maven-plugin - 2.2 + 2.3 etc/copyright.txt etc/copyright-exclude.txt diff --git a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java index d5ee75df5..57948a4bf 100644 --- a/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java +++ b/src/main/java/org/eclipse/yasson/internal/ComponentMatcher.java @@ -319,7 +319,7 @@ private Type resolveTypeArg(Type adapterTypeArg, Type adapterType) { return ReflectionUtils.resolveTypeArguments((ParameterizedType) adapterTypeArg, adapterType); } else if (adapterTypeArg instanceof TypeVariable) { return ReflectionUtils - .resolveItemVariableType(new RuntimeTypeHolder(null, adapterType), (TypeVariable) adapterTypeArg); + .resolveItemVariableType(new RuntimeTypeHolder(null, adapterType), (TypeVariable) adapterTypeArg, true); } else { return adapterTypeArg; } diff --git a/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java b/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java index 60155c778..9d5cc8d6d 100644 --- a/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java +++ b/src/main/java/org/eclipse/yasson/internal/JsonBindingBuilder.java @@ -12,6 +12,8 @@ package org.eclipse.yasson.internal; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import javax.json.bind.Jsonb; @@ -25,17 +27,24 @@ public class JsonBindingBuilder implements JsonbBuilder { private JsonbConfig config = new JsonbConfig(); private JsonProvider provider = null; + private JsonBinding bindingCache = null; + private Map configCache = null; + private JsonProvider providerCache = null; @Override public JsonbBuilder withConfig(JsonbConfig config) { - this.config = config; - return this; + synchronized (this) { + this.config = config; + return this; + } } @Override public JsonbBuilder withProvider(JsonProvider jsonpProvider) { - this.provider = jsonpProvider; - return this; + synchronized (this) { + this.provider = jsonpProvider; + return this; + } } /** @@ -58,6 +67,33 @@ public Optional getProvider() { @Override public Jsonb build() { - return new JsonBinding(this); + synchronized (this) { + if (bindingCache != null + && configEqualsCachedConfig() + && providerEqualsCachedProvider()) { + return bindingCache; + } + JsonBinding jsonBinding = new JsonBinding(this); + cacheCurrentConfiguration(jsonBinding); + return jsonBinding; + } + } + + private boolean configEqualsCachedConfig() { + return (configCache != null && config != null && configCache.equals(config.getAsMap())) + || (config == null && configCache == null); + } + + private boolean providerEqualsCachedProvider() { + return (providerCache != null && providerCache.equals(provider)) + || (provider == null && providerCache == null); + } + + private void cacheCurrentConfiguration(JsonBinding jsonBinding) { + bindingCache = jsonBinding; + if (config != null) { + configCache = new HashMap<>(config.getAsMap()); + } + providerCache = provider; } } diff --git a/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java index bdba8418a..a1d786a9f 100644 --- a/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java +++ b/src/main/java/org/eclipse/yasson/internal/ReflectionUtils.java @@ -99,7 +99,7 @@ public static Class resolveRawType(RuntimeTypeInfo item, Type type) { return getRawType(resolveType(item, type)); } } - + /** * Resolve a type by item. * If type is a {@link TypeVariable} recursively search {@link AbstractItem} for resolution of typevar. @@ -111,10 +111,14 @@ public static Class resolveRawType(RuntimeTypeInfo item, Type type) { * @return resolved type */ public static Type resolveType(RuntimeTypeInfo item, Type type) { + return resolveType(item, type, true); + } + + private static Type resolveType(RuntimeTypeInfo item, Type type, boolean warn) { if (type instanceof WildcardType) { - return resolveMostSpecificBound(item, (WildcardType) type); + return resolveMostSpecificBound(item, (WildcardType) type, warn); } else if (type instanceof TypeVariable) { - return resolveItemVariableType(item, (TypeVariable) type); + return resolveItemVariableType(item, (TypeVariable) type, warn); } else if (type instanceof ParameterizedType && item != null) { return resolveTypeArguments((ParameterizedType) type, item.getRuntimeType()); } @@ -130,33 +134,36 @@ public static Type resolveType(RuntimeTypeInfo item, Type type) { */ public static Optional resolveOptionalType(RuntimeTypeInfo info, Type type) { try { - return Optional.of(resolveType(info, type)); + return Optional.of(resolveType(info, type, false)); } catch (RuntimeException e) { return Optional.empty(); } } - + /** * Resolve a bounded type variable type by its wrapper types. * Resolution could be done only if a compile time generic information is provided, either: * by generic field or subclass of a generic class. * + * @param whether or not to log a warning message when bounds are not found * @param item item to search "runtime" generic type of a TypeVariable. * @param typeVariable type to search in item for, not null. * @return Type of a generic "runtime" bound, not null. */ - public static Type resolveItemVariableType(RuntimeTypeInfo item, TypeVariable typeVariable) { + static Type resolveItemVariableType(RuntimeTypeInfo item, TypeVariable typeVariable, boolean warn) { if (item == null) { //Bound not found, treat it as an Object.class - LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, - typeVariable, - typeVariable.getGenericDeclaration())); + if (warn) { + LOGGER.warning(Messages.getMessage(MessageKeys.GENERIC_BOUND_NOT_FOUND, + typeVariable, + typeVariable.getGenericDeclaration())); + } return Object.class; } //Embedded items doesn't hold information about variable types if (item instanceof EmbeddedItem) { - return resolveItemVariableType(item.getWrapper(), typeVariable); + return resolveItemVariableType(item.getWrapper(), typeVariable, warn); } ParameterizedType wrapperParameterizedType = findParameterizedSuperclass(item.getRuntimeType()); @@ -165,12 +172,12 @@ public static Type resolveItemVariableType(RuntimeTypeInfo item, TypeVariable Type foundType = search.searchParametrizedType(wrapperParameterizedType, typeVariable); if (foundType != null) { if (foundType instanceof TypeVariable) { - return resolveItemVariableType(item.getWrapper(), (TypeVariable) foundType); + return resolveItemVariableType(item.getWrapper(), (TypeVariable) foundType, warn); } return foundType; } - return resolveItemVariableType(item.getWrapper(), typeVariable); + return resolveItemVariableType(item.getWrapper(), typeVariable, warn); } /** @@ -314,23 +321,23 @@ private static ParameterizedType findParameterizedSuperclass(Type type) { * @param wildcardType Wildcard type. * @return The most specific type. */ - private static Type resolveMostSpecificBound(RuntimeTypeInfo item, WildcardType wildcardType) { + private static Type resolveMostSpecificBound(RuntimeTypeInfo item, WildcardType wildcardType, boolean warn) { Class result = Object.class; for (Type upperBound : wildcardType.getUpperBounds()) { - result = getMostSpecificBound(item, result, upperBound); + result = getMostSpecificBound(item, result, upperBound, warn); } for (Type lowerBound : wildcardType.getLowerBounds()) { - result = getMostSpecificBound(item, result, lowerBound); + result = getMostSpecificBound(item, result, lowerBound, warn); } return result; } - private static Class getMostSpecificBound(RuntimeTypeInfo item, Class result, Type bound) { + private static Class getMostSpecificBound(RuntimeTypeInfo item, Class result, Type bound, boolean warn) { if (bound == Object.class) { return result; } //if bound is type variable search recursively for wrapper generic expansion - Type resolvedBoundType = bound instanceof TypeVariable ? resolveType(item, bound) : bound; + Type resolvedBoundType = bound instanceof TypeVariable ? resolveType(item, bound, warn) : bound; Class boundRawType = getRawType(resolvedBoundType); //resolved class is a subclass of a result candidate if (result.isAssignableFrom(boundRawType)) { diff --git a/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotated.java b/src/main/java/org/eclipse/yasson/internal/model/JsonbAnnotated.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java index c386facac..e3d6706d2 100644 --- a/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java +++ b/src/main/java/org/eclipse/yasson/internal/serializer/ObjectSerializer.java @@ -59,15 +59,24 @@ public ObjectSerializer(CurrentItem wrapper, Type runtimeType, ClassModel cla @Override protected void serializeInternal(T object, JsonGenerator generator, SerializationContext ctx) { - final PropertyModel[] allProperties = ((Marshaller) ctx).getMappingContext().getOrCreateClassModel(object.getClass()) - .getSortedProperties(); - for (PropertyModel model : allProperties) { - try { - marshallProperty(object, generator, ctx, model); - } catch (Exception e) { - throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_PROPERTY_ERROR, model.getWriteName(), - object.getClass().getCanonicalName()), e); + Marshaller context = (Marshaller) ctx; + try { + if (context.addProcessedObject(object)) { + final PropertyModel[] allProperties = context.getMappingContext().getOrCreateClassModel(object.getClass()) + .getSortedProperties(); + for (PropertyModel model : allProperties) { + try { + marshallProperty(object, generator, context, model); + } catch (Exception e) { + throw new JsonbException(Messages.getMessage(MessageKeys.SERIALIZE_PROPERTY_ERROR, model.getWriteName(), + object.getClass().getCanonicalName()), e); + } + } + } else { + throw new JsonbException(Messages.getMessage(MessageKeys.RECURSIVE_REFERENCE, object.getClass())); } + } finally { + context.removeProcessedObject(object); } } diff --git a/src/main/resources/META-INF/native-image/native-image.properties b/src/main/resources/META-INF/native-image/native-image.properties index 57fb92152..abee70613 100644 --- a/src/main/resources/META-INF/native-image/native-image.properties +++ b/src/main/resources/META-INF/native-image/native-image.properties @@ -1,15 +1,14 @@ -################################################################################ +# # Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. +# # This program and the accompanying materials are made available under the -# terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 -# which accompanies this distribution. -# The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html -# and the Eclipse Distribution License is available at +# terms of the Eclipse Public License v. 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, +# or the Eclipse Distribution License v. 1.0 which is available at # http://www.eclipse.org/org/documents/edl-v10.php. # -# Contributors: -# Tomas Langer -################################################################################ +# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +# # This adds command line options to native image tool Args=-H:IncludeResourceBundles=yasson-messages \ No newline at end of file diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Chain.java b/src/test/java/org/eclipse/yasson/adapters/model/Chain.java new file mode 100644 index 000000000..b69870ee8 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/Chain.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +public class Chain { + + private String name; + private Chain linksTo; + private Foo has; + + public Chain(String name) { + this.name = name; + } + + public Chain() { + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public Chain getLinksTo() { + return linksTo; + } + public void setLinksTo(Chain linksTo) { + this.linksTo = linksTo; + } + public Foo getHas() { + return has; + } + public void setHas(Foo has) { + this.has = has; + } + +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java new file mode 100644 index 000000000..d15230a8f --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/ChainAdapter.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.json.bind.adapter.JsonbAdapter; + +public class ChainAdapter implements JsonbAdapter>{ + + @Override + public Map adaptToJson(Chain obj) throws Exception { + Map map = new LinkedHashMap<>(); + map.put("has", obj.getHas()); + map.put("linksTo", obj.getLinksTo()); + map.put("name", obj.getName()); + return map; + } + + @SuppressWarnings("unchecked") + @Override + public Chain adaptFromJson(Map obj) throws Exception { + if(obj != null) { + Chain chain = new Chain((String) obj.get("name")); + chain.setHas((Foo) obj.get("has")); + adaptFromJson((Map) obj.get("linksTo")); + return chain; + } else { + return null; + } + + } + +} \ No newline at end of file diff --git a/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java new file mode 100644 index 000000000..92247adc4 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/ChainSerializer.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +import javax.json.bind.serializer.JsonbSerializer; +import javax.json.bind.serializer.SerializationContext; +import javax.json.stream.JsonGenerator; + +public class ChainSerializer implements JsonbSerializer{ + + public static final String RECURSIVE_REFERENCE_ERROR = "There is a recursive reference"; + + @Override + public void serialize(Chain obj, JsonGenerator generator, SerializationContext ctx) { + generator.writeStartObject(); + if(obj.getHas() != null) { + ctx.serialize("has", obj.getHas(), generator); + } + if(obj.getLinksTo() != null) { + ctx.serialize("linksTo", obj.getLinksTo(), generator); + } + generator.write("name", obj.getName()); + generator.writeEnd(); + } + +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/Foo.java b/src/test/java/org/eclipse/yasson/adapters/model/Foo.java new file mode 100644 index 000000000..99341ed36 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/Foo.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +public class Foo { + + private String bar; + + public Foo(String bar) { + this.bar = bar; + } + + public String getBar() { + return bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java b/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java new file mode 100644 index 000000000..59197643e --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/FooAdapter.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +import java.util.HashMap; +import java.util.Map; + +import javax.json.bind.adapter.JsonbAdapter; + +public class FooAdapter implements JsonbAdapter>{ + + @Override + public Map adaptToJson(Foo obj) throws Exception { + Map map = new HashMap<>(); + map.put("bar", obj.getBar()); + return map; + } + + @Override + public Foo adaptFromJson( Map obj) throws Exception { + return new Foo(obj.get("bar").toString()); + } + +} diff --git a/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java b/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java new file mode 100644 index 000000000..32766915d --- /dev/null +++ b/src/test/java/org/eclipse/yasson/adapters/model/FooSerializer.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.adapters.model; + +import javax.json.bind.serializer.JsonbSerializer; +import javax.json.bind.serializer.SerializationContext; +import javax.json.stream.JsonGenerator; + +public class FooSerializer implements JsonbSerializer{ + + @Override + public void serialize(Foo obj, JsonGenerator generator, SerializationContext ctx) { + generator.writeStartObject(); + generator.write("bar", obj.getBar()); + generator.writeEnd(); + } + +} diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java new file mode 100644 index 000000000..b1340edca --- /dev/null +++ b/src/test/java/org/eclipse/yasson/defaultmapping/specific/RecursiveReferenceTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.defaultmapping.specific; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import java.util.Arrays; + +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbBuilder; +import javax.json.bind.JsonbConfig; +import javax.json.bind.JsonbException; + +import org.eclipse.yasson.Jsonbs; +import org.eclipse.yasson.adapters.model.Chain; +import org.eclipse.yasson.adapters.model.ChainAdapter; +import org.eclipse.yasson.adapters.model.ChainSerializer; +import org.eclipse.yasson.adapters.model.Foo; +import org.eclipse.yasson.adapters.model.FooAdapter; +import org.eclipse.yasson.adapters.model.FooSerializer; +import org.junit.jupiter.api.Test; + +public class RecursiveReferenceTest { + + private static final Jsonb userSerializerJsonb = JsonbBuilder.create(new JsonbConfig() + .withSerializers(new ChainSerializer(), new FooSerializer())); + private static final Jsonb adapterSerializerJsonb = JsonbBuilder.create(new JsonbConfig() + .withAdapters(new ChainAdapter(), new FooAdapter())); + + @Test + public void testSerializeRecursiveReference() { + Chain recursive = new Chain("test"); + recursive.setLinksTo(recursive); + try { + Jsonbs.defaultJsonb.toJson(recursive); + fail("Exception should be caught"); + } catch (JsonbException e) { + assertEquals( + "Unable to serialize property 'linksTo' from org.eclipse.yasson.adapters.model.Chain", + e.getMessage()); + assertEquals( + "Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.", + e.getCause().getMessage()); + } + } + + @Test + public void testSerializeRecursiveReferenceCustomAdapter() { + Chain recursive = new Chain("test"); + recursive.setLinksTo(recursive); + try { + adapterSerializerJsonb.toJson(recursive); + fail("Exception should be caught"); + } catch (JsonbException e) { + assertEquals( + "Problem adapting object of type class org.eclipse.yasson.adapters.model.Chain to java.util.Map in class class org.eclipse.yasson.adapters.model.ChainAdapter", + e.getMessage()); + } + } + + @Test + public void testSerializeRecursiveReferenceCustomSerializer() { + Chain recursive = new Chain("test"); + recursive.setLinksTo(recursive); + try { + userSerializerJsonb.toJson(recursive); + fail("Exception should be caught"); + } catch (JsonbException e) { + assertEquals("Recursive reference has been found in class class org.eclipse.yasson.adapters.model.Chain.", e.getMessage()); + } + } + + @Test + public void testSerializeRepeatedInstance() { + checkSerializeRepeatedInstance(Jsonbs.defaultJsonb); + checkSerializeRepeatedInstance(adapterSerializerJsonb); + checkSerializeRepeatedInstance(userSerializerJsonb); + } + + private void checkSerializeRepeatedInstance(Jsonb jsonb) { + Chain recursive = new Chain("test"); + recursive.setLinksTo(new Chain("test")); + String result = jsonb.toJson(Arrays.asList(recursive, recursive)); + assertEquals("[{\"linksTo\":{\"name\":\"test\"},\"name\":\"test\"},{\"linksTo\":{\"name\":\"test\"},\"name\":\"test\"}]", result); + } + + @Test + public void testSerialize2ReferencesSameObject() { + A a = new A(); + Foo b = new Foo("foo"); + a.ref1 = b; + a.ref2 = b; + String result = Jsonbs.defaultJsonb.toJson(a); + assertEquals("{\"ref1\":{\"bar\":\"foo\"},\"ref2\":{\"bar\":\"foo\"}}", result); + } + + @Test + public void testChain() { + checkChain(Jsonbs.defaultJsonb); + checkChain(adapterSerializerJsonb); + checkChain(userSerializerJsonb); + } + + private void checkChain(Jsonb jsonb) { + Foo foo = new Foo("foo"); + Chain c1 = new Chain("c1"); + Chain c2 = new Chain("c2"); + c1.setLinksTo(c2); + c1.setHas(foo); + c2.setHas(foo); + String result = jsonb.toJson(c1); + assertEquals("{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"name\":\"c2\"},\"name\":\"c1\"}", result); + } + + @Test + public void testDeeperChain() { + checkDeeperChain(Jsonbs.defaultJsonb); + checkDeeperChain(adapterSerializerJsonb); + checkDeeperChain(userSerializerJsonb); + } + + private void checkDeeperChain(Jsonb jsonb) { + Foo foo = new Foo("foo"); + Chain c1 = new Chain("c1"); + Chain c2 = new Chain("c2"); + Chain c3 = new Chain("c3"); + c1.setLinksTo(c2); + c1.setHas(foo); + c2.setHas(foo); + c2.setLinksTo(c3); + String result = jsonb.toJson(c1); + assertEquals("{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"has\":{\"bar\":\"foo\"},\"linksTo\":{\"name\":\"c3\"},\"name\":\"c2\"},\"name\":\"c1\"}", result); + } + + public static class A { + public Foo ref1; + public Foo ref2; + } + +} diff --git a/src/test/java/org/eclipse/yasson/internal/JsonBindingBuilderTest.java b/src/test/java/org/eclipse/yasson/internal/JsonBindingBuilderTest.java new file mode 100644 index 000000000..747a5e452 --- /dev/null +++ b/src/test/java/org/eclipse/yasson/internal/JsonBindingBuilderTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ + +package org.eclipse.yasson.internal; + +import org.glassfish.json.JsonProviderImpl; +import org.junit.jupiter.api.Test; + +import javax.json.bind.Jsonb; +import javax.json.bind.JsonbConfig; + +import static org.junit.jupiter.api.Assertions.*; + +public class JsonBindingBuilderTest { + + @Test + public void testMultipleCallsToBuildWithoutChangesReturnTheSameInstance() { + JsonBindingBuilder builder = new JsonBindingBuilder(); + + Jsonb jsonb1 = builder.build(); + Jsonb jsonb2 = builder.build(); + + assertSame(jsonb1, jsonb2); + } + + @Test + public void testMultipleCallsToBuildWithEqualConfigReturnTheSameInstance() { + JsonBindingBuilder builder = new JsonBindingBuilder(); + JsonbConfig config = new JsonbConfig(); + + Jsonb jsonb1 = builder.build(); + builder.withConfig(config); + Jsonb jsonb2 = builder.build(); + + assertSame(jsonb1, jsonb2); + } + + + @Test + public void testMultipleCallsToBuildWithChangedConfigReturnNotTheSameInstance() { + JsonBindingBuilder builder = new JsonBindingBuilder(); + JsonbConfig config = new JsonbConfig(); + builder.withConfig(config); + + Jsonb jsonb1 = builder.build(); + config.withStrictIJSON(true); + Jsonb jsonb2 = builder.build(); + + assertNotSame(jsonb1, jsonb2); + } + + + @Test + public void testMultipleCallsToBuildWithChangedProviderReturnNotTheSameInstance() { + JsonBindingBuilder builder = new JsonBindingBuilder(); + + Jsonb jsonb1 = builder.build(); + builder.withProvider(new JsonProviderImpl()); + Jsonb jsonb2 = builder.build(); + + assertNotSame(jsonb1, jsonb2); + } +}