You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@johnzon.apache.org by rm...@apache.org on 2021/01/25 10:54:41 UTC

[johnzon] branch master updated (a6e569e -> 343c831)

This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git.


    from a6e569e  1.2.10 download page update
     new 4f53103  [JOHNZON-332] exception de/serialization
     new 0d06bd0  [JOHNZON-332] add skipAccessModeWrapper toggle for backward compatibility
     new 343c831  [JOHNZON-332] merge jsonb builder with new skipAccessModeWrapper toggle for consistency

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/johnzon/jsonb/JohnzonBuilder.java   |   8 +-
 .../org/apache/johnzon/jsonb/JsonbWriteTest.java   |  31 ++
 .../org/apache/johnzon/mapper/MapperBuilder.java   |  10 +
 .../access/KnownNotOpenedJavaTypesAccessMode.java  | 415 +++++++++++++++++++++
 .../johnzon/mapper/CircularExceptionTest.java      |   4 +-
 5 files changed, 465 insertions(+), 3 deletions(-)
 create mode 100644 johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/KnownNotOpenedJavaTypesAccessMode.java


[johnzon] 02/03: [JOHNZON-332] add skipAccessModeWrapper toggle for backward compatibility

Posted by rm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git

commit 0d06bd02fb9e8e3d9cc26dc90dd004e20b0b3b0a
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Mon Jan 25 11:51:16 2021 +0100

    [JOHNZON-332] add skipAccessModeWrapper toggle for backward compatibility
---
 .../src/main/java/org/apache/johnzon/mapper/MapperBuilder.java    | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index cbb2213..9646a90 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -106,6 +106,7 @@ public class MapperBuilder {
     private boolean useBigDecimalForObjectNumbers;
     private boolean supportEnumContainerDeserialization = true;
     private Function<Class<?>, MapperConfig.CustomEnumConverter<?>> enumConverterFactory = type -> new EnumConverter(type);
+    private boolean skipAccessModeWrapper;
 
     // @experimental polymorphic api
     private Function<String, Class<?>> typeLoader;
@@ -200,7 +201,7 @@ public class MapperBuilder {
                 throw new IllegalStateException("AccessMode is not an BaseAccessMode");
             }
         }
-        if (!KnownNotOpenedJavaTypesAccessMode.class.isInstance(accessMode)) {
+        if (!skipAccessModeWrapper && !KnownNotOpenedJavaTypesAccessMode.class.isInstance(accessMode)) {
             accessMode = new KnownNotOpenedJavaTypesAccessMode(accessMode);
         }
 
@@ -561,4 +562,9 @@ public class MapperBuilder {
         this.discriminator = value;
         return this;
     }
+
+    public MapperBuilder setSkipAccessModeWrapper(final boolean skipAccessModeWrapper) {
+        this.skipAccessModeWrapper = skipAccessModeWrapper;
+        return this;
+    }
 }


[johnzon] 03/03: [JOHNZON-332] merge jsonb builder with new skipAccessModeWrapper toggle for consistency

Posted by rm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git

commit 343c8318c0236a7fa04f308261c4a4b59cd60745
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Mon Jan 25 11:54:35 2021 +0100

    [JOHNZON-332] merge jsonb builder with new skipAccessModeWrapper toggle for consistency
---
 .../org/apache/johnzon/jsonb/JohnzonBuilder.java   | 41 ++++++++++------------
 1 file changed, 19 insertions(+), 22 deletions(-)

diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index 52878dc..0b749a4 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -38,7 +38,6 @@ import org.apache.johnzon.mapper.ObjectConverter;
 import org.apache.johnzon.mapper.SerializeValueFilter;
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
-import org.apache.johnzon.mapper.access.KnownNotOpenedJavaTypesAccessMode;
 import org.apache.johnzon.mapper.converter.LocaleConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 
@@ -212,29 +211,27 @@ public class JohnzonBuilder implements JsonbBuilder {
             throw new IllegalArgumentException("Unsupported factory: " + val);
         }).orElseGet(() -> findFactory(skipCdi));
 
+        ofNullable(config.getProperty("johnzon.skip-exception-serialization"))
+                .map(v -> Boolean.parseBoolean(String.valueOf(v)))
+                .ifPresent(builder::setSkipAccessModeWrapper);
+
         final AccessMode accessMode = config.getProperty("johnzon.accessMode")
                 .map(this::toAccessMode)
-                .orElseGet(() -> {
-                    final AccessMode access = new JsonbAccessMode(
-                            propertyNamingStrategy, orderValue, visibilityStrategy,
-                            !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
-                            builder.getAdapters(),
-                            factory, jsonp, builderFactorySupplier, parserFactoryProvider,
-                            config.getProperty("johnzon.accessModeDelegate")
-                                    .map(this::toAccessMode)
-                                    .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, true)),
-                            config.getProperty("johnzon.failOnMissingCreatorValues")
-                                    .map(this::toBool)
-                                    .orElse(true) /*spec 1.0 requirement*/,
-                            isNillable,
-                            config.getProperty("johnzon.supportsPrivateAccess")
-                                    .map(this::toBool)
-                                    .orElse(false));
-                    return ofNullable(config.getProperty("johnzon.enable-exception-serialization"))
-                            .map(v -> Boolean.parseBoolean(String.valueOf(v)))
-                            .map(it -> it ? new KnownNotOpenedJavaTypesAccessMode(access) : access)
-                            .orElseGet(() -> new KnownNotOpenedJavaTypesAccessMode(access));
-                });
+                .orElseGet(() -> new JsonbAccessMode(
+                        propertyNamingStrategy, orderValue, visibilityStrategy,
+                        !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
+                        builder.getAdapters(),
+                        factory, jsonp, builderFactorySupplier, parserFactoryProvider,
+                        config.getProperty("johnzon.accessModeDelegate")
+                                .map(this::toAccessMode)
+                                .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, true)),
+                        config.getProperty("johnzon.failOnMissingCreatorValues")
+                                .map(this::toBool)
+                                .orElse(true) /*spec 1.0 requirement*/,
+                        isNillable,
+                        config.getProperty("johnzon.supportsPrivateAccess")
+                                .map(this::toBool)
+                                .orElse(false)));
         builder.setAccessMode(accessMode);
 
         // user adapters


[johnzon] 01/03: [JOHNZON-332] exception de/serialization

Posted by rm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git

commit 4f53103f64c9f7c0ec645c1b0d04c1d34f9a8dcf
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Mon Jan 25 11:48:36 2021 +0100

    [JOHNZON-332] exception de/serialization
---
 .../org/apache/johnzon/jsonb/JohnzonBuilder.java   |  37 +-
 .../org/apache/johnzon/jsonb/JsonbWriteTest.java   |  31 ++
 .../org/apache/johnzon/mapper/MapperBuilder.java   |   4 +
 .../access/KnownNotOpenedJavaTypesAccessMode.java  | 415 +++++++++++++++++++++
 .../johnzon/mapper/CircularExceptionTest.java      |   4 +-
 5 files changed, 475 insertions(+), 16 deletions(-)

diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
index ab82053..52878dc 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JohnzonBuilder.java
@@ -38,6 +38,7 @@ import org.apache.johnzon.mapper.ObjectConverter;
 import org.apache.johnzon.mapper.SerializeValueFilter;
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
+import org.apache.johnzon.mapper.access.KnownNotOpenedJavaTypesAccessMode;
 import org.apache.johnzon.mapper.converter.LocaleConverter;
 import org.apache.johnzon.mapper.internal.AdapterKey;
 
@@ -213,21 +214,27 @@ public class JohnzonBuilder implements JsonbBuilder {
 
         final AccessMode accessMode = config.getProperty("johnzon.accessMode")
                 .map(this::toAccessMode)
-                .orElseGet(() -> new JsonbAccessMode(
-                        propertyNamingStrategy, orderValue, visibilityStrategy,
-                        !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
-                        builder.getAdapters(),
-                        factory, jsonp, builderFactorySupplier, parserFactoryProvider,
-                        config.getProperty("johnzon.accessModeDelegate")
-                                .map(this::toAccessMode)
-                                .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, true)),
-                        config.getProperty("johnzon.failOnMissingCreatorValues")
-                              .map(this::toBool)
-                              .orElse(true) /*spec 1.0 requirement*/,
-                        isNillable,
-                        config.getProperty("johnzon.supportsPrivateAccess")
-                                .map(this::toBool)
-                                .orElse(false)));
+                .orElseGet(() -> {
+                    final AccessMode access = new JsonbAccessMode(
+                            propertyNamingStrategy, orderValue, visibilityStrategy,
+                            !namingStrategyValue.orElse("").equals(PropertyNamingStrategy.CASE_INSENSITIVE),
+                            builder.getAdapters(),
+                            factory, jsonp, builderFactorySupplier, parserFactoryProvider,
+                            config.getProperty("johnzon.accessModeDelegate")
+                                    .map(this::toAccessMode)
+                                    .orElseGet(() -> new FieldAndMethodAccessMode(true, true, false, true)),
+                            config.getProperty("johnzon.failOnMissingCreatorValues")
+                                    .map(this::toBool)
+                                    .orElse(true) /*spec 1.0 requirement*/,
+                            isNillable,
+                            config.getProperty("johnzon.supportsPrivateAccess")
+                                    .map(this::toBool)
+                                    .orElse(false));
+                    return ofNullable(config.getProperty("johnzon.enable-exception-serialization"))
+                            .map(v -> Boolean.parseBoolean(String.valueOf(v)))
+                            .map(it -> it ? new KnownNotOpenedJavaTypesAccessMode(access) : access)
+                            .orElseGet(() -> new KnownNotOpenedJavaTypesAccessMode(access));
+                });
         builder.setAccessMode(accessMode);
 
         // user adapters
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbWriteTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbWriteTest.java
index 95541ee..36b41a5 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbWriteTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/JsonbWriteTest.java
@@ -22,8 +22,11 @@ import org.junit.Test;
 
 import javax.json.bind.Jsonb;
 import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.annotation.JsonbCreator;
 import javax.json.bind.annotation.JsonbDateFormat;
 import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.config.PropertyOrderStrategy;
 import javax.json.bind.spi.JsonbProvider;
 import java.io.ByteArrayOutputStream;
 import java.io.StringWriter;
@@ -36,10 +39,30 @@ import java.util.Map;
 import java.util.TreeMap;
 
 import static java.util.Arrays.asList;
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
 public class JsonbWriteTest {
     @Test
+    public void throwable() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
+                .withPropertyOrderStrategy(PropertyOrderStrategy.LEXICOGRAPHICAL))){
+            final IllegalStateException exception = new IllegalStateException("oops");
+            exception.setStackTrace(new StackTraceElement[]{
+                    new StackTraceElement("foo", "bar", "dummy", 1),
+                    new StackTraceElement("foo2", "bar2", "dummy2", 2)
+            });
+            final String json = jsonb.toJson(exception, Throwable.class);
+            final Throwable throwable = jsonb.fromJson(json, Throwable.class);
+            assertEquals(exception.getMessage(), throwable.getMessage());
+            assertArrayEquals(exception.getStackTrace(), throwable.getStackTrace());
+            assertEquals("{\"message\":\"oops\"," +
+                    "\"stackTrace\":[{\"className\":\"foo\",\"fileName\":\"dummy\",\"lineNumber\":1,\"methodName\":\"bar\"}," +
+                    "{\"className\":\"foo2\",\"fileName\":\"dummy2\",\"lineNumber\":2,\"methodName\":\"bar2\"}]}", json);
+        }
+    }
+
+    @Test
     public void mapOfSimple() throws Exception {
         final Map<String, Simple> list = new TreeMap<>();
         list.put("1", new Simple());
@@ -201,5 +224,13 @@ public class JsonbWriteTest {
         }
     }
 
+    public static class CustomException extends RuntimeException {
+        private final int code;
 
+        @JsonbCreator
+        public CustomException(final String message, final int code) {
+            super(message);
+            this.code = code;
+        }
+    }
 }
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
index 171f93a..cbb2213 100644
--- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MapperBuilder.java
@@ -28,6 +28,7 @@ import org.apache.johnzon.mapper.access.BaseAccessMode;
 import org.apache.johnzon.mapper.access.FieldAccessMode;
 import org.apache.johnzon.mapper.access.FieldAndMethodAccessMode;
 import org.apache.johnzon.mapper.access.MethodAccessMode;
+import org.apache.johnzon.mapper.access.KnownNotOpenedJavaTypesAccessMode;
 import org.apache.johnzon.mapper.converter.BooleanConverter;
 import org.apache.johnzon.mapper.converter.ByteConverter;
 import org.apache.johnzon.mapper.converter.CachedDelegateConverter;
@@ -199,6 +200,9 @@ public class MapperBuilder {
                 throw new IllegalStateException("AccessMode is not an BaseAccessMode");
             }
         }
+        if (!KnownNotOpenedJavaTypesAccessMode.class.isInstance(accessMode)) {
+            accessMode = new KnownNotOpenedJavaTypesAccessMode(accessMode);
+        }
 
         if (primitiveConverters) {
             adapters.put(new AdapterKey(Byte.class, String.class), new ConverterAdapter<>(new CachedDelegateConverter<>(new ByteConverter()), Byte.class));
diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/KnownNotOpenedJavaTypesAccessMode.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/KnownNotOpenedJavaTypesAccessMode.java
new file mode 100644
index 0000000..917ef9c
--- /dev/null
+++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/access/KnownNotOpenedJavaTypesAccessMode.java
@@ -0,0 +1,415 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.mapper.access;
+
+import org.apache.johnzon.mapper.Adapter;
+import org.apache.johnzon.mapper.ObjectConverter;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+import static java.util.Collections.emptyMap;
+
+// on last java releases, throwable, stacktraceelement etc are no more opened by default
+// so hardcode the model we want for serialization/deserialization
+//
+// Note: it is still a bad habit to serialize exceptions but it happens
+public class KnownNotOpenedJavaTypesAccessMode implements AccessMode {
+    private final AccessMode delegate;
+
+    public KnownNotOpenedJavaTypesAccessMode(final AccessMode delegate) {
+        this.delegate = delegate;
+    }
+
+    private boolean isInaccessibleAndThrowable(final Class<?> clazz, final RuntimeException ioe) {
+        return "java.lang.reflect.InaccessibleObjectException".equals(ioe.getClass().getName()) &&
+                Throwable.class.isAssignableFrom(clazz);
+    }
+
+    private Map<String, Writer> findThrowableWriters(final Class<?> clazz) {
+        final Map<String, Writer> map = new HashMap<>();
+        // map.put("message", ...); // constructor
+        map.put("stackTrace", new InlineWriter<>(Throwable::setStackTrace, StackTraceElement[].class));
+        // todo: if (SubTypeException.class == clazz) addSpecificFields(map);
+        return map;
+    }
+
+    private Map<String, Reader> findThrowableReaders(final Class<?> clazz) {
+        final Map<String, Reader> map = new HashMap<>();
+        map.put("message", new InlineReader<>(Throwable::getMessage, String.class));
+        map.put("stackTrace", new InlineReader<>(Throwable::getStackTrace, StackTraceElement[].class));
+        // todo: if (SubTypeException.class == clazz) addSpecificFields(map);
+        return map;
+    }
+
+    private Map<String, Reader> findStackTraceElementReaders(final Class<?> clazz) {
+        final Map<String, Reader> map = new HashMap<>();
+        map.put("className", new InlineReader<>(StackTraceElement::getClassName, String.class));
+        map.put("methodName", new InlineReader<>(StackTraceElement::getMethodName, String.class));
+        map.put("fileName", new InlineReader<>(StackTraceElement::getFileName, String.class));
+        map.put("lineNumber", new InlineReader<>(StackTraceElement::getLineNumber, int.class));
+        return map;
+    }
+
+    // for now we only support "roots" and delegate to system "openness" the others
+    // but strictly speaking we can add all java.* exception there
+    //
+    // in practise exceptions are generally serialized but not deserialized so "ok-ish"
+    private Factory findThrowableFactory(final Class<?> clazz) {
+        if (Throwable.class == clazz) {
+            return new ExceptionMessageFactory() {
+                @Override
+                public Object create(final Object[] params) {
+                    return new Throwable(String.class.cast(params[0]));
+                }
+            };
+        }
+        if (Exception.class == clazz) {
+            return new ExceptionMessageFactory() {
+                @Override
+                public Object create(final Object[] params) {
+                    return new Exception(String.class.cast(params[0]));
+                }
+            };
+        }
+        if (Error.class == clazz) {
+            return new ExceptionMessageFactory() {
+                @Override
+                public Object create(final Object[] params) {
+                    return new Error(String.class.cast(params[0]));
+                }
+            };
+        }
+        // todo: add more
+        try {
+            return delegate.findFactory(clazz);
+        } catch (final RuntimeException | Error e) {
+            return null;
+        }
+    }
+
+    private boolean isJavaThrowable(final Class<?> clazz) {
+        return clazz.getName().startsWith("java.") && Throwable.class.isAssignableFrom(clazz);
+    }
+
+    private boolean isStackTraceElement(final Class<?> clazz) {
+        return clazz == StackTraceElement.class;
+    }
+
+    @Override
+    public Factory findFactory(final Class<?> clazz, final Function<AnnotatedElement, String>... parameterNameExtractors) {
+        return delegate.findFactory(clazz, parameterNameExtractors);
+    }
+
+    @Override
+    public Factory findFactory(final Class<?> clazz) {
+        if (isJavaThrowable(clazz)) {
+            return findThrowableFactory(clazz);
+        }
+        if (isStackTraceElement(clazz)) {
+            return new BaseFactory() {
+                @Override
+                public Object create(final Object[] params) {
+                    return new StackTraceElement(
+                            String.class.cast(params[0]),
+                            String.class.cast(params[1]),
+                            String.class.cast(params[2]),
+                            params.length < 4 || params[3] == null ? -1 : Integer.class.cast(params[3]));
+                }
+
+                @Override
+                public Type[] getParameterTypes() {
+                    return new Type[]{String.class, String.class, String.class, int.class};
+                }
+
+                @Override
+                public String[] getParameterNames() {
+                    return new String[]{"className", "methodName", "fileName", "lineNumber"};
+                }
+
+                @Override
+                public Adapter<?, ?>[] getParameterConverter() {
+                    return new Adapter[]{null, null, null, null};
+                }
+
+                @Override
+                public Adapter<?, ?>[] getParameterItemConverter() {
+                    return new Adapter[]{null, null, null, null};
+                }
+
+                @Override
+                public ObjectConverter.Codec<?>[] getObjectConverter() {
+                    return new ObjectConverter.Codec[]{null, null, null, null};
+                }
+            };
+        }
+        return delegate.findFactory(clazz);
+    }
+
+    @Override
+    public Map<String, Reader> findReaders(final Class<?> clazz) {
+        if (isJavaThrowable(clazz)) {
+            return findThrowableReaders(clazz);
+        }
+        if (isStackTraceElement(clazz)) {
+            return findStackTraceElementReaders(clazz);
+        }
+        try {
+            return delegate.findReaders(clazz);
+        } catch (final RuntimeException ioe) {
+            if (isInaccessibleAndThrowable(clazz, ioe)) {
+                return findThrowableReaders(clazz);
+            }
+            throw ioe;
+        }
+    }
+
+    @Override
+    public Map<String, Writer> findWriters(final Class<?> clazz) {
+        if (isJavaThrowable(clazz)) {
+            return findThrowableWriters(clazz);
+        }
+        if (isStackTraceElement(clazz)) {
+            return emptyMap();
+        }
+        try {
+            return delegate.findWriters(clazz);
+        } catch (final RuntimeException ioe) {
+            if (isInaccessibleAndThrowable(clazz, ioe)) {
+                return findThrowableWriters(clazz);
+            }
+            throw ioe;
+        }
+    }
+
+    @Override
+    public ObjectConverter.Reader<?> findReader(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findReader(clazz);
+    }
+
+    @Override
+    public ObjectConverter.Writer<?> findWriter(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findWriter(clazz);
+    }
+
+    @Override
+    public Method findAnyGetter(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findAnyGetter(clazz);
+    }
+
+    @Override
+    public Method findAnySetter(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findAnySetter(clazz);
+    }
+
+    @Override
+    public Field findAnyField(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findAnyField(clazz);
+    }
+
+    @Override
+    public Method findMapAdder(final Class<?> clazz) {
+        if (isJavaThrowable(clazz) || isStackTraceElement(clazz)) {
+            return null;
+        }
+        return delegate.findMapAdder(clazz);
+    }
+
+    @Override
+    public Adapter<?, ?> findAdapter(final Class<?> clazz) {
+        return delegate.findAdapter(clazz);
+    }
+
+    @Override
+    public Comparator<String> fieldComparator(final Class<?> clazz) {
+        return delegate.fieldComparator(clazz);
+    }
+
+    @Override
+    public void afterParsed(final Class<?> clazz) {
+        delegate.afterParsed(clazz);
+    }
+
+    private static class InlineWriter<T, F> implements Writer {
+        private final BiConsumer setter;
+        private final Type type;
+
+        private InlineWriter(final BiConsumer<T, F> setter, final Type type) {
+            this.setter = setter;
+            this.type = type;
+        }
+
+        @Override
+        public Type getType() {
+            return type;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            return null;
+        }
+
+        @Override
+        public boolean isNillable(final boolean globalConfig) {
+            return false;
+        }
+
+        @Override
+        public void write(final Object instance, final Object value) {
+            setter.accept(instance, value);
+        }
+
+        @Override
+        public ObjectConverter.Reader<?> findObjectConverterReader() {
+            return null;
+        }
+    }
+
+    private static class InlineReader<T, F> implements Reader {
+        private final Function accessor;
+        private final Class<F> type;
+
+        private InlineReader(final Function<T, F> accessor, final Class<F> type) {
+            this.accessor = Function.class.cast(accessor);
+            this.type = type;
+        }
+
+        @Override
+        public Type getType() {
+            return type;
+        }
+
+        @Override
+        public <T extends Annotation> T getAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public <T extends Annotation> T getClassOrPackageAnnotation(final Class<T> clazz) {
+            return null;
+        }
+
+        @Override
+        public Adapter<?, ?> findConverter() {
+            return null;
+        }
+
+        @Override
+        public boolean isNillable(final boolean globalConfig) {
+            return false;
+        }
+
+        @Override
+        public Object read(final Object instance) {
+            return accessor.apply(instance);
+        }
+
+        @Override
+        public ObjectConverter.Writer<?> findObjectConverterWriter() {
+            return null;
+        }
+    }
+
+    private static abstract class BaseFactory implements Factory {
+        @Override
+        public Type[] getParameterTypes() {
+            return new Type[0];
+        }
+
+        @Override
+        public String[] getParameterNames() {
+            return new String[0];
+        }
+
+        @Override
+        public Adapter<?, ?>[] getParameterConverter() {
+            return new Adapter[0];
+        }
+
+        @Override
+        public Adapter<?, ?>[] getParameterItemConverter() {
+            return new Adapter[0];
+        }
+
+        @Override
+        public ObjectConverter.Codec<?>[] getObjectConverter() {
+            return new ObjectConverter.Codec[0];
+        }
+    }
+
+    private static abstract class ExceptionMessageFactory extends BaseFactory {
+        @Override
+        public Type[] getParameterTypes() {
+            return new Type[]{String.class};
+        }
+
+        @Override
+        public String[] getParameterNames() {
+            return new String[]{"message"};
+        }
+
+        @Override
+        public Adapter<?, ?>[] getParameterConverter() {
+            return new Adapter[]{null};
+        }
+
+        @Override
+        public Adapter<?, ?>[] getParameterItemConverter() {
+            return new Adapter[]{null};
+        }
+
+        @Override
+        public ObjectConverter.Codec<?>[] getObjectConverter() {
+            return new ObjectConverter.Codec[]{null};
+        }
+    }
+}
diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
index d6da4cb..1b44da0 100644
--- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
+++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java
@@ -23,13 +23,15 @@ import org.junit.Test;
 import static org.junit.Assert.assertTrue;
 
 public class CircularExceptionTest {
+    // note that with KnownNotOpenedJavaTypes this test will not test circular case anymore
+    // but we still care to test exceptions don't loop so kept it
     @Test
     public void dontStackOverFlow() {
         final Throwable oopsImVicous = new Exception("circular");
         oopsImVicous.getStackTrace(); // fill it
         oopsImVicous.initCause(new IllegalArgumentException(oopsImVicous));
         final String serialized = new MapperBuilder().setAccessModeName("field").build().writeObjectAsString(oopsImVicous);
-        assertTrue(serialized.contains("\"detailMessage\":\"circular\""));
+        assertTrue(serialized.contains("\"message\":\"circular\""));
         assertTrue(serialized.contains("\"stackTrace\":[{"));
     }