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