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);
+ }
+}