Merge branch 'master' of https://gitbox.apache.org/repos/asf/johnzon
diff --git a/.travis.yml b/.travis.yml
index e0b4fe6..eae0453 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,10 @@
 sudo: false
 
 jdk:
-  - oraclejdk8 
-before_install: mvn clean install -DskipTests=true
-after_success:
-  - mvn clean cobertura:cobertura coveralls:report
+  - openjdk8
+  - openjdk11
+
+env:
+  global:
+    - MAVEN_OPTS="-Dmaven.artifact.threads=128"
+
diff --git a/NOTICE b/NOTICE
index cb43ecb..e1cbd0e 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Johnzon
-Copyright 2014-2018 The Apache Software Foundation
+Copyright 2014-2019 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/johnzon-core/pom.xml b/johnzon-core/pom.xml
index f589f8f..4bc1a1b 100644
--- a/johnzon-core/pom.xml
+++ b/johnzon-core/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java b/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
index 40194bd..ded93e3 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/AbstractJsonFactory.java
@@ -78,4 +78,14 @@
         return Boolean.parseBoolean(boolValue.toString());
     }
 
+    protected String getString(final String key, final String defaultValue) {
+        final Object value = internalConfig.get(key);
+        if (value == null) {
+            return defaultValue;
+        } else if (String.class.isInstance(value)) {
+            return String.class.cast(value);
+        }
+        return value.toString();
+    }
+
 }
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
index af2aa25..5ea8c65 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonArrayBuilderImpl.java
@@ -298,7 +298,7 @@
         addValue(builder.build());
         return this;
     }
-    
+
     private void setValue(int idx, JsonValue value) {
         if (value == null || tmpList == null) {
             throw npe();
@@ -333,7 +333,7 @@
     @Override
     public JsonArray build() {
         if(tmpList == null) {
-            return new JsonArrayImpl(Collections.emptyList(), bufferProvider);
+            return JsonValue.EMPTY_JSON_ARRAY;
         }
         return new JsonArrayImpl(Collections.unmodifiableList(tmpList), bufferProvider);
     }
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
index de13a0a..fafcc74 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonObjectBuilderImpl.java
@@ -176,16 +176,16 @@
         if(name == null || value == null) {
             throw new NullPointerException("name or value/builder must not be null");
         }
-        
+
         attributeMap.put(name, value);
     }
-    
+
 
     @Override
     public JsonObject build() {
-        
+
         if(attributeMap == null || attributeMap.isEmpty()) {
-            return new JsonObjectImpl(Collections.EMPTY_MAP, bufferProvider);
+            return JsonValue.EMPTY_JSON_OBJECT;
         } else {
             Map<String, JsonValue> dump = (Collections.unmodifiableMap(attributeMap));
             return new JsonObjectImpl(dump, bufferProvider);
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
index 437a2ca..a1651df 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonParserFactoryImpl.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 
 import static java.util.Arrays.asList;
+import static java.util.Optional.ofNullable;
 
 public class JsonParserFactoryImpl extends AbstractJsonFactory implements JsonParserFactory {
     public static final String MAX_STRING_LENGTH = "org.apache.johnzon.max-string-length";
@@ -40,10 +41,11 @@
     public static final int DEFAULT_BUFFER_LENGTH = Integer.getInteger(BUFFER_LENGTH, 64 * 1024); //64k
     
     public static final String SUPPORTS_COMMENTS = "org.apache.johnzon.supports-comments";
+    public static final String ENCODING = "org.apache.johnzon.encoding";
     public static final boolean DEFAULT_SUPPORTS_COMMENT = Boolean.getBoolean(SUPPORTS_COMMENTS); //default is false;
 
     static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
-        BUFFER_STRATEGY, MAX_STRING_LENGTH, BUFFER_LENGTH, SUPPORTS_COMMENTS, AUTO_ADJUST_STRING_BUFFER
+        BUFFER_STRATEGY, MAX_STRING_LENGTH, BUFFER_LENGTH, SUPPORTS_COMMENTS, AUTO_ADJUST_STRING_BUFFER, ENCODING
     );
       
     private final int maxSize;
@@ -51,6 +53,7 @@
     private final BufferStrategy.BufferProvider<char[]> valueBufferProvider;
     private final boolean supportsComments;
     private final boolean autoAdjustBuffers;
+    private final Charset defaultEncoding;
 
     JsonParserFactoryImpl(final Map<String, ?> config) {
         super(config, SUPPORTED_CONFIG_KEYS, null);
@@ -65,9 +68,13 @@
         this.valueBufferProvider = getBufferProvider().newCharProvider(maxSize);
         this.supportsComments = getBool(SUPPORTS_COMMENTS, DEFAULT_SUPPORTS_COMMENT);
         this.autoAdjustBuffers = getBool(AUTO_ADJUST_STRING_BUFFER, true);
+        this.defaultEncoding = ofNullable(getString(ENCODING, null)).map(Charset::forName).orElse(null);
     }
 
     private JsonStreamParserImpl getDefaultJsonParserImpl(final InputStream in) {
+        if (defaultEncoding != null) {
+            return getDefaultJsonParserImpl(in, defaultEncoding);
+        }
         if (supportsComments) {
             return new CommentsJsonStreamParserImpl(in, maxSize, bufferProvider, valueBufferProvider, autoAdjustBuffers);
         }
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
index 71bd0e1..24de161 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonReaderImpl.java
@@ -72,7 +72,7 @@
         checkClosed();
 
         if (!parser.hasNext()) {
-            throw new IllegalStateException("Nothing to read");
+            throw new NothingToRead();
         }
 
 
@@ -282,4 +282,10 @@
         }
 
     }
+
+    public static class NothingToRead extends IllegalStateException {
+        public NothingToRead() {
+            super("Nothing to read");
+        }
+    }
 }
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
index fd05752..bf4a2c0 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonStreamParserImpl.java
@@ -21,8 +21,10 @@
 import javax.json.JsonException;
 import javax.json.stream.JsonLocation;
 import javax.json.stream.JsonParsingException;
+
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.math.BigDecimal;
 import java.nio.charset.Charset;
@@ -149,8 +151,10 @@
 
         if (reader != null) {
             this.in = reader;
-        } else {
-            this.in = new RFC4627AwareInputStreamReader(inputStream, encoding);
+        } else if (encoding != null) { // always respect it
+            this.in = new InputStreamReader(inputStream, encoding);
+        } else { // should we log the usage is unexpected? (for perf we want to avoid the pushbackinputstream)
+            this.in = new RFC4627AwareInputStreamReader(inputStream);
         }
     }
 
@@ -207,7 +211,7 @@
         if (previousEvent != END_ARRAY && previousEvent != END_OBJECT &&
                 previousEvent != VALUE_STRING && previousEvent != VALUE_FALSE && previousEvent != VALUE_TRUE &&
                 previousEvent != VALUE_NULL && previousEvent != VALUE_NUMBER) {
-            if (bufferPos == Integer.MIN_VALUE) { // check we don't have an empty string to parse
+            if (bufferPos < 0) { // check we don't have an empty string to parse
                 final char c = readNextChar();
                 bufferPos--;
                 return c != EOF;
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java b/johnzon-core/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
index 0fc2784..c3d0c37 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/RFC4627AwareInputStreamReader.java
@@ -29,15 +29,12 @@
 
 final class RFC4627AwareInputStreamReader extends InputStreamReader {
 
-    /**
-     * @param preferredCharset the Charset to use if no BOM is used. If {@code null} use UTF-8
-     */
-    RFC4627AwareInputStreamReader(final InputStream in, Charset preferredCharset) {
-        this(new PushbackInputStream(in,4), preferredCharset);
+    RFC4627AwareInputStreamReader(final InputStream in) {
+        this(new PushbackInputStream(in,4));
     }
 
-    private RFC4627AwareInputStreamReader(final PushbackInputStream in, Charset preferredCharset) {
-        super(in, getCharset(in, preferredCharset).newDecoder());
+    private RFC4627AwareInputStreamReader(final PushbackInputStream in) {
+        super(in, getCharset(in).newDecoder());
     }
 
     /**
@@ -86,8 +83,8 @@
 
         */
 
-    private static Charset getCharset(final PushbackInputStream inputStream, Charset preferredCharset) {
-        Charset charset = preferredCharset != null ? preferredCharset : StandardCharsets.UTF_8;
+    private static Charset getCharset(final PushbackInputStream inputStream) {
+        Charset charset = StandardCharsets.UTF_8;
         int bomLength=0;
         try {
             final byte[] utfBytes = readAllBytes(inputStream);
diff --git a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
index e41cdbe..becdf5f 100644
--- a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
+++ b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonReaderImplTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.johnzon.core;
 
+import static java.util.Collections.emptyMap;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -425,6 +426,13 @@
         reader.close();
     }
 
+    @Test(expected = JsonReaderImpl.NothingToRead.class)
+    public void emptyStream() {
+        Json.createReaderFactory(emptyMap())
+                .createReader(new ByteArrayInputStream(new byte[0]), utf8Charset)
+                .readObject();
+    }
+
     @Test(expected = IllegalArgumentException.class)
     public void emptyZeroCharBuffersize() {
         final JsonReader reader = Json.createReaderFactory(new HashMap<String, Object>() {
diff --git a/johnzon-distribution/pom.xml b/johnzon-distribution/pom.xml
index eaf1f8c..0e4287f 100644
--- a/johnzon-distribution/pom.xml
+++ b/johnzon-distribution/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-jaxrs/pom.xml b/johnzon-jaxrs/pom.xml
index d91a067..c530446 100644
--- a/johnzon-jaxrs/pom.xml
+++ b/johnzon-jaxrs/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java
index 3ab6682..9b853db 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/ConfigurableJohnzonProvider.java
@@ -41,14 +41,13 @@
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
-import javax.ws.rs.ext.Provider;
 
 import org.apache.johnzon.mapper.MapperBuilder;
 import org.apache.johnzon.mapper.SerializeValueFilter;
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.access.BaseAccessMode;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JohnzonProvider
 @Produces("application/json")
 @Consumes("application/json")
 public class ConfigurableJohnzonProvider<T> implements MessageBodyWriter<T>, MessageBodyReader<T> {
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/DelegateProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/DelegateProvider.java
index aee0790..21a2b0e 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/DelegateProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/DelegateProvider.java
@@ -18,6 +18,8 @@
  */
 package org.apache.johnzon.jaxrs;
 
+import static java.util.Optional.ofNullable;
+
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
@@ -33,7 +35,8 @@
     private final MessageBodyWriter<T> writer;
 
     protected DelegateProvider(final MessageBodyReader<T> reader, final MessageBodyWriter<T> writer) {
-        this.reader = reader;
+        this.reader = shouldThrowNoContentExceptionOnEmptyStreams() && isJaxRs2() ?
+                new NoContentExceptionHandlerReader<>(reader) : reader;
         this.writer = writer;
     }
 
@@ -70,4 +73,19 @@
                         final OutputStream entityStream) throws IOException {
         writer.writeTo(t, rawType, genericType, annotations, mediaType, httpHeaders, entityStream);
     }
+
+    protected boolean shouldThrowNoContentExceptionOnEmptyStreams() {
+        return false;
+    }
+
+    private static boolean isJaxRs2() {
+        try {
+            ofNullable(Thread.currentThread().getContextClassLoader())
+                    .orElseGet(ClassLoader::getSystemClassLoader)
+                    .loadClass("javax.ws.rs.core.Feature");
+            return true;
+        } catch (final NoClassDefFoundError | ClassNotFoundException e) {
+            return false;
+        }
+    }
 }
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java
index 42fcbf0..b65349d 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyReader.java
@@ -27,7 +27,6 @@
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.MessageBodyReader;
-import javax.ws.rs.ext.Provider;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
@@ -36,7 +35,7 @@
 import java.lang.reflect.Type;
 import java.util.Collection;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JohnzonProvider
 @Consumes({
     "application/json", "*/json",
     "*/*+json", "*/x-json",
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java
index 6a3e163..9c7b1ac 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonMessageBodyWriter.java
@@ -28,7 +28,6 @@
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.ext.MessageBodyWriter;
-import javax.ws.rs.ext.Provider;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -37,7 +36,7 @@
 import java.lang.reflect.Type;
 import java.util.Collection;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JohnzonProvider
 @Produces({
     "application/json", "*/json",
     "*/*+json", "*/x-json",
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonProvider.java
index b1e137f..b99e42b 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JohnzonProvider.java
@@ -31,10 +31,14 @@
 @Consumes("application/json")
 public class JohnzonProvider<T> extends DelegateProvider<T> {
     public JohnzonProvider(final Mapper mapper, final Collection<String> ignores) {
-        super(new JohnzonMessageBodyReader<T>(mapper, ignores), new JohnzonMessageBodyWriter<T>(mapper, ignores));
+        super(new JohnzonMessageBodyReader<>(mapper, ignores), new JohnzonMessageBodyWriter<>(mapper, ignores));
     }
 
     public JohnzonProvider() {
         this(new MapperBuilder().setDoCloseOnStreams(false).build(), null);
     }
+
+    protected boolean shouldThrowNoContentExceptionOnEmptyStreams() {
+        return Boolean.getBoolean("johnzon.jaxrs.johnzon.throwNoContentExceptionOnEmptyStreams");
+    }
 }
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyReader.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyReader.java
index 7a48bd4..69b0825 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyReader.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyReader.java
@@ -27,7 +27,6 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
-import javax.ws.rs.ext.Provider;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
@@ -36,7 +35,7 @@
 
 import static org.apache.johnzon.mapper.internal.Streams.noClose;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JsrProvider
 @Consumes({
     "application/json", "*/json",
     "*/*+json", "*/x-json",
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyWriter.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyWriter.java
index 6d23c3b..b6163c2 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyWriter.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrMessageBodyWriter.java
@@ -27,7 +27,6 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyWriter;
-import javax.ws.rs.ext.Provider;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.annotation.Annotation;
@@ -36,7 +35,7 @@
 
 import static org.apache.johnzon.mapper.internal.Streams.noClose;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JsrProvider
 @Produces({
     "application/json", "*/json",
     "*/*+json", "*/x-json",
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrProvider.java
index 06ec86d..872dfe9 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/JsrProvider.java
@@ -18,9 +18,16 @@
  */
 package org.apache.johnzon.jaxrs;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
 import javax.json.JsonStructure;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.Provider;
 
 @Provider
@@ -30,4 +37,16 @@
     public JsrProvider() {
         super(new JsrMessageBodyReader(), new JsrMessageBodyWriter());
     }
+
+    @Override
+    public JsonStructure readFrom(final Class<JsonStructure> rawType, final Type genericType,
+                                  final Annotation[] annotations, final MediaType mediaType,
+                                  final MultivaluedMap<String, String> httpHeaders,
+                                  final InputStream entityStream) throws IOException {
+        return super.readFrom(rawType, genericType, annotations, mediaType, httpHeaders, entityStream);
+    }
+
+    protected boolean shouldThrowNoContentExceptionOnEmptyStreams() {
+        return Boolean.getBoolean("johnzon.jaxrs.jsr.throwNoContentExceptionOnEmptyStreams");
+    }
 }
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/NoContentExceptionHandlerReader.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/NoContentExceptionHandlerReader.java
new file mode 100644
index 0000000..acd496d
--- /dev/null
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/NoContentExceptionHandlerReader.java
@@ -0,0 +1,63 @@
+/*
+ * 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.jaxrs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NoContentException;
+import javax.ws.rs.ext.MessageBodyReader;
+
+public class NoContentExceptionHandlerReader<T> implements MessageBodyReader<T> {
+    private final MessageBodyReader<T> delegate;
+
+    public NoContentExceptionHandlerReader(final MessageBodyReader<T> delegate) {
+        this.delegate = delegate;
+    }
+
+    public MessageBodyReader<T> getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public boolean isReadable(final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType) {
+        return delegate.isReadable(type, genericType, annotations, mediaType);
+    }
+
+    @Override
+    public T readFrom(final Class<T> type, final Type genericType, final Annotation[] annotations,
+                      final MediaType mediaType, final MultivaluedMap<String, String> httpHeaders,
+                      final InputStream entityStream) throws IOException, WebApplicationException {
+        try {
+            return delegate.readFrom(type, genericType, annotations, mediaType, httpHeaders, entityStream);
+        } catch (final IllegalStateException ise) {
+            if (ise.getClass().getName()
+                    .equals("org.apache.johnzon.core.JsonReaderImpl$NothingToRead")) {
+                // spec enables to return an empty java object but it does not mean anything in JSON context so just fail
+                throw new NoContentException(ise);
+            }
+            throw ise;
+        }
+    }
+}
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
index aa71150..5fc52fa 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardConfigurableJohnzonProvider.java
@@ -31,7 +31,6 @@
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
-import javax.ws.rs.ext.Provider;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -44,7 +43,7 @@
 
 import static java.util.Arrays.asList;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with WildcardJohnzonProvider
 @Produces({
         "*/json",
         "*/*+json", "*/x-json",
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJohnzonProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJohnzonProvider.java
index c573fff..585622d 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJohnzonProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJohnzonProvider.java
@@ -26,7 +26,7 @@
 import javax.ws.rs.ext.Provider;
 import java.util.Collection;
 
-@Provider
+@Provider // this is scanned cause does not overlap with JohnzonProvider in terms of mime types
 @Produces({
     "*/json",
     "*/*+json", "*/x-json",
@@ -45,4 +45,8 @@
     public WildcardJohnzonProvider() {
         this(new MapperBuilder().setDoCloseOnStreams(false).build(), null);
     }
+
+    protected boolean shouldThrowNoContentExceptionOnEmptyStreams() {
+        return Boolean.getBoolean("johnzon.jaxrs.johnzon.wildcard.throwNoContentExceptionOnEmptyStreams");
+    }
 }
diff --git a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJsrProvider.java b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJsrProvider.java
index 66d3791..657a69e 100644
--- a/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJsrProvider.java
+++ b/johnzon-jaxrs/src/main/java/org/apache/johnzon/jaxrs/WildcardJsrProvider.java
@@ -21,9 +21,8 @@
 import javax.json.JsonStructure;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Produces;
-import javax.ws.rs.ext.Provider;
 
-@Provider
+// @Provider // don't let it be scanned, it would conflict with JsrProvider
 @Produces({
     "*/json",
     "*/*+json", "*/x-json",
@@ -38,4 +37,8 @@
     public WildcardJsrProvider() {
         super(new JsrMessageBodyReader(), new JsrMessageBodyWriter());
     }
+
+    protected boolean shouldThrowNoContentExceptionOnEmptyStreams() {
+        return Boolean.getBoolean("johnzon.jaxrs.jsr.wildcard.throwNoContentExceptionOnEmptyStreams");
+    }
 }
diff --git a/johnzon-json-extras/pom.xml b/johnzon-json-extras/pom.xml
index 92f6c3f..74ca0d2 100644
--- a/johnzon-json-extras/pom.xml
+++ b/johnzon-json-extras/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-jsonb/pom.xml b/johnzon-jsonb/pom.xml
index 0a478bb..13b0c85 100644
--- a/johnzon-jsonb/pom.xml
+++ b/johnzon-jsonb/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java
index 30bf771..20d547a 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProvider.java
@@ -18,6 +18,7 @@
  */
 package org.apache.johnzon.jaxrs.jsonb.jaxrs;
 
+import static java.util.Optional.ofNullable;
 import static java.util.stream.Collectors.toMap;
 
 import javax.json.JsonStructure;
@@ -29,6 +30,7 @@
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.NoContentException;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.StreamingOutput;
 import javax.ws.rs.ext.MessageBodyReader;
@@ -63,7 +65,9 @@
     protected final Collection<String> ignores;
     protected final JsonbConfig config = new JsonbConfig();
     protected volatile Function<Class<?>, Jsonb> delegate = null;
+    protected volatile ReadImpl readImpl = null;
     private boolean customized;
+    private Boolean throwNoContentExceptionOnEmptyStreams;
 
     @Context
     private Providers providers;
@@ -80,6 +84,11 @@
         return ignores != null && ignores.contains(type.getName());
     }
 
+    public void setThrowNoContentExceptionOnEmptyStreams(final boolean throwNoContentExceptionOnEmptyStreams) {
+        this.throwNoContentExceptionOnEmptyStreams = throwNoContentExceptionOnEmptyStreams;
+        // customized = false since it is not a jsonb customization but a MBR one
+    }
+
     // config - main containers support the configuration of providers this way
     public void setFailOnUnknownProperties(final boolean active) {
         config.setProperty("johnzon.fail-on-unknown-properties", active);
@@ -187,13 +196,14 @@
 
     @Override
     public T readFrom(final Class<T> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType,
-            final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws IOException, WebApplicationException {
-        return getJsonb(type).fromJson(entityStream, genericType);
+            final MultivaluedMap<String, String> httpHeaders, final InputStream entityStream) throws WebApplicationException, IOException {
+        final Jsonb jsonb = getJsonb(type);
+        return (T) readImpl.doRead(jsonb, genericType, entityStream);
     }
 
     @Override
     public void writeTo(final T t, final Class<?> type, final Type genericType, final Annotation[] annotations, final MediaType mediaType,
-            final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws IOException, WebApplicationException {
+            final MultivaluedMap<String, Object> httpHeaders, final OutputStream entityStream) throws WebApplicationException {
         getJsonb(type).toJson(t, entityStream);
     }
 
@@ -205,22 +215,63 @@
         if (delegate == null){
             synchronized (this) {
                 if (delegate == null) {
+                    if (throwNoContentExceptionOnEmptyStreams == null) {
+                        throwNoContentExceptionOnEmptyStreams = initThrowNoContentExceptionOnEmptyStreams();
+                    }
                     final ContextResolver<Jsonb> contextResolver = providers.getContextResolver(Jsonb.class, MediaType.APPLICATION_JSON_TYPE);
                     if (contextResolver != null) {
                         if (customized) {
-                            Logger.getLogger(JsonbJaxrsProvider.class.getName())
-                                  .warning("Customizations done on the Jsonb instance will be ignored because a ContextResolver<Jsonb> was found");
+                            logger().warning("Customizations done on the Jsonb instance will be ignored because a ContextResolver<Jsonb> was found");
+                        }
+                        if (throwNoContentExceptionOnEmptyStreams) {
+                            logger().warning("Using a ContextResolver<Jsonb>, NoContentException will not be thrown for empty streams");
                         }
                         delegate = new DynamicInstance(contextResolver); // faster than contextResolver::getContext
                     } else {
-                        delegate = new ProvidedInstance(createJsonb()); // don't recreate it
+                        // don't recreate it for perfs
+                        delegate = new ProvidedInstance(createJsonb());
                     }
                 }
+                readImpl = throwNoContentExceptionOnEmptyStreams ?
+                        this::doReadWithNoContentException :
+                        this::doRead;
             }
         }
         return delegate.apply(type);
     }
 
+    private boolean initThrowNoContentExceptionOnEmptyStreams() {
+        try {
+            ofNullable(Thread.currentThread().getContextClassLoader())
+                    .orElseGet(ClassLoader::getSystemClassLoader)
+                    .loadClass("javax.ws.rs.core.Feature");
+            return true;
+        } catch (final NoClassDefFoundError | ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    private Object doRead(final Jsonb jsonb, final Type t, final InputStream stream) {
+        return jsonb.fromJson(stream, t);
+    }
+
+    private Object doReadWithNoContentException(final Jsonb jsonb, final Type t, final InputStream stream) throws NoContentException {
+        try {
+            return doRead(jsonb, t, stream);
+        } catch (final IllegalStateException ise) {
+            if (ise.getClass().getName()
+                    .equals("org.apache.johnzon.core.JsonReaderImpl$NothingToRead")) {
+                // spec enables to return an empty java object but it does not mean anything in JSON context so just fail
+                throw new NoContentException(ise);
+            }
+            throw ise;
+        }
+    }
+
+    private Logger logger() {
+        return Logger.getLogger(JsonbJaxrsProvider.class.getName());
+    }
+
     @Override
     public synchronized void close() throws Exception {
         if (AutoCloseable.class.isInstance(delegate)) {
@@ -228,6 +279,10 @@
         }
     }
 
+    private interface ReadImpl {
+        Object doRead(Jsonb jsonb, Type type, InputStream entityStream) throws IOException;
+    }
+
     private static final class DynamicInstance implements Function<Class<?>, Jsonb> {
         private final ContextResolver<Jsonb> contextResolver;
 
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
index 89f85a5..4d19dff 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/JsonbAccessMode.java
@@ -868,8 +868,10 @@
         ReaderConverters(final DecoratedType annotationHolder) {
             final boolean numberType = isNumberType(annotationHolder.getType());
             final boolean dateType = isDateType(annotationHolder.getType());
+            final boolean hasRawType = hasRawType(annotationHolder.getType());
             final JsonbTypeDeserializer deserializer = annotationHolder.getAnnotation(JsonbTypeDeserializer.class);
             final JsonbTypeAdapter adapter = annotationHolder.getAnnotation(JsonbTypeAdapter.class);
+            final JsonbTypeAdapter typeAdapter = hasRawType ? getRawType(annotationHolder.getType()).getDeclaredAnnotation(JsonbTypeAdapter.class) : null;
             JsonbDateFormat dateFormat = dateType ? annotationHolder.getAnnotation(JsonbDateFormat.class) : null;
             JsonbNumberFormat numberFormat = numberType ? annotationHolder.getAnnotation(JsonbNumberFormat.class) : null;
             final JohnzonConverter johnzonConverter = annotationHolder.getAnnotation(JohnzonConverter.class);
@@ -881,9 +883,9 @@
                 numberFormat = annotationHolder.getClassOrPackageAnnotation(JsonbNumberFormat.class);
             }
 
-            converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
+            converter = adapter == null && typeAdapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
                     defaultConverters.get(new AdapterKey(annotationHolder.getType(), String.class)) :
-                    toConverter(types, annotationHolder.getType(), adapter, dateFormat, numberFormat);
+                    toConverter(types, annotationHolder.getType(), adapter != null ? adapter : typeAdapter, dateFormat, numberFormat);
 
             if (deserializer != null) {
                 final Class<? extends JsonbDeserializer> value = deserializer.value();
@@ -966,12 +968,14 @@
         WriterConverters(final DecoratedType reader, final Types types) {
             final boolean numberType = isNumberType(reader.getType());
             final boolean dateType = isDateType(reader.getType());
+            final boolean hasRawType = hasRawType(reader.getType());
             final JsonbTypeSerializer serializer = reader.getAnnotation(JsonbTypeSerializer.class);
             final JsonbTypeAdapter adapter = reader.getAnnotation(JsonbTypeAdapter.class);
+            final JsonbTypeAdapter typeAdapter = hasRawType ? getRawType(reader.getType()).getDeclaredAnnotation(JsonbTypeAdapter.class) : null;
             JsonbDateFormat dateFormat = dateType ? reader.getAnnotation(JsonbDateFormat.class) : null;
             JsonbNumberFormat numberFormat = numberType ? reader.getAnnotation(JsonbNumberFormat.class) : null;
             final JohnzonConverter johnzonConverter = reader.getAnnotation(JohnzonConverter.class);
-            validateAnnotations(reader, adapter, dateFormat, numberFormat, johnzonConverter);
+            validateAnnotations(reader, adapter != null ? adapter : typeAdapter, dateFormat, numberFormat, johnzonConverter);
             if (dateFormat == null && isDateType(reader.getType())) {
                 dateFormat = reader.getClassOrPackageAnnotation(JsonbDateFormat.class);
             }
@@ -979,9 +983,9 @@
                 numberFormat = reader.getClassOrPackageAnnotation(JsonbNumberFormat.class);
             }
 
-            converter = adapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
+            converter = adapter == null && typeAdapter == null && dateFormat == null && numberFormat == null && johnzonConverter == null ?
                     defaultConverters.get(new AdapterKey(reader.getType(), String.class)) :
-                    toConverter(types, reader.getType(), adapter, dateFormat, numberFormat);
+                    toConverter(types, reader.getType(), adapter != null ? adapter : typeAdapter, dateFormat, numberFormat);
 
             if (serializer != null) {
                 final Class<? extends JsonbSerializer> value = serializer.value();
@@ -1033,6 +1037,20 @@
         return Number.class.isAssignableFrom(clazz) || clazz.isPrimitive();
     }
 
+    private boolean hasRawType(final Type type) {
+        return Class.class.isInstance(type) ||
+            (ParameterizedType.class.isInstance(type) &&
+                    Class.class.isInstance(ParameterizedType.class.cast(type).getRawType()));
+    }
+
+    private Class<?> getRawType(final Type type) { // only intended to be used after hasRawType check
+        if (Class.class.isInstance(type)) {
+            return Class.class.cast(type);
+        }
+        // ParameterizedType + Class raw type
+        return Class.class.cast(ParameterizedType.class.cast(type).getRawType());
+    }
+
     private static class ClassDecoratedType implements DecoratedType {
         private final Class<?> annotations;
 
diff --git a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
index a3b58dd..c531449 100644
--- a/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
+++ b/johnzon-jsonb/src/main/java/org/apache/johnzon/jsonb/serializer/JohnzonDeserializationContext.java
@@ -63,6 +63,10 @@
                 parseObject(null, parser, objectBuilder);
                 return objectBuilder.build();
             }
+            case END_OBJECT:
+                return JsonValue.EMPTY_JSON_OBJECT;
+            case END_ARRAY:
+                return JsonValue.EMPTY_JSON_ARRAY;
             case START_ARRAY:
                 final JsonArrayBuilder arrayBuilder = builderFactory.createArrayBuilder();
                 parseArray(parser, arrayBuilder);
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProviderTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProviderTest.java
new file mode 100644
index 0000000..ae14878
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jaxrs/jsonb/jaxrs/JsonbJaxrsProviderTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jaxrs.jsonb.jaxrs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.NoContentException;
+import javax.ws.rs.ext.ContextResolver;
+
+import org.apache.cxf.jaxrs.impl.ProvidersImpl;
+import org.apache.johnzon.core.JsonReaderImpl;
+import org.junit.Test;
+
+public class JsonbJaxrsProviderTest {
+    @Test(expected = NoContentException.class)
+    public void noContentExceptionAuto() throws IOException { // we run on jaxrs 2 in the build
+        readFoo(null, new ByteArrayInputStream(new byte[0]));
+    }
+
+    @Test(expected = NoContentException.class)
+    public void noContentException() throws IOException {
+        readFoo(true, new ByteArrayInputStream(new byte[0]));
+    }
+
+    @Test(expected = JsonReaderImpl.NothingToRead.class)
+    public void noContentExceptionDisabled() throws IOException {
+        readFoo(false, new ByteArrayInputStream(new byte[0]));
+    }
+
+    @Test // just to ensure we didnt break soemthing on read impl
+    public void validTest() throws IOException {
+        final Foo foo = readFoo(null, new ByteArrayInputStream("{\"name\":\"ok\"}".getBytes(StandardCharsets.UTF_8)));
+        assertEquals("ok", foo.name);
+    }
+
+    private Foo readFoo(final Boolean set, final InputStream stream) throws IOException {
+        return new JsonbJaxrsProvider<Foo>() {{
+            if (set != null) {
+                setThrowNoContentExceptionOnEmptyStreams(set);
+            }
+            setProviders(this);
+        }}.readFrom(Foo.class, Foo.class, new Annotation[0],
+                MediaType.APPLICATION_JSON_TYPE, new MultivaluedHashMap<>(),
+                stream);
+    }
+
+    private void setProviders(final JsonbJaxrsProvider<Foo> provider) {
+        try {
+            final Field providers = JsonbJaxrsProvider.class.getDeclaredField("providers");
+            providers.setAccessible(true);
+            providers.set(provider, new ProvidersImpl(null) {
+                @Override
+                public <T> ContextResolver<T> getContextResolver(final Class<T> contextType, final MediaType mediaType) {
+                    return null;
+                }
+            });
+        } catch (final Exception e) {
+            fail(e.getMessage());
+        }
+    }
+
+    public static class Foo {
+        public String name;
+    }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
index 8e12cbd..dca4576 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/AdapterTest.java
@@ -1,3 +1,4 @@
+package org.apache.johnzon.jsonb;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements. See the NOTICE file
@@ -16,7 +17,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.johnzon.jsonb;
+
 
 import org.junit.Test;
 
@@ -32,7 +33,11 @@
 import java.util.List;
 
 import static java.util.Arrays.asList;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.endsWith;
+import static org.hamcrest.CoreMatchers.startsWith;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 public class AdapterTest {
@@ -44,13 +49,32 @@
             foo.bar.value = 1;
             foo.dummy = new Dummy();
             foo.dummy.value = 2L;
+            foo.baz = new Baz();
+            foo.baz.value = "3";
 
             final String toString = jsonb.toJson(foo);
-            assertEquals("{\"bar\":\"1\",\"dummy\":\"2\"}", toString);
+            assertThat(toString, startsWith("{"));
+            assertThat(toString, containsString("\"bar\":\"1\""));
+            assertThat(toString, containsString("\"dummy\":\"2\""));
+            assertThat(toString, containsString("\"baz\":\"3\""));
+            assertThat(toString, endsWith("}"));
+            assertEquals("{\"bar\":\"1\",\"dummy\":\"2\",\"baz\":\"3\"}".length(), toString.length());
 
             final Foo read = jsonb.fromJson(toString, Foo.class);
             assertEquals(foo.bar.value, read.bar.value);
             assertEquals(foo.dummy.value, read.dummy.value);
+            assertEquals(foo.baz.value, read.baz.value);
+        }
+    }
+
+    @Test
+    public void adaptValue() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create()) {
+            final Baz baz = new Baz();
+            baz.value = "test";
+            
+            final String toString = jsonb.toJson(baz);
+            assertEquals("\"test\"", toString);
         }
     }
 
@@ -144,6 +168,8 @@
 
         @JsonbTypeAdapter(DummyAdapter.class)
         public Dummy dummy;
+        
+        public Baz baz;
     }
 
     public static class Bar2 extends Bar {
@@ -169,6 +195,27 @@
         }
     }
 
+    @JsonbTypeAdapter(BazAdapter.class)
+    public static class Baz {
+        public String value;
+    }
+
+    public static class BazAdapter implements JsonbAdapter<Baz, String> {
+
+        @Override
+        public String adaptToJson(Baz obj) throws Exception {
+            return obj.value;
+        }
+
+        @Override
+        public Baz adaptFromJson(String obj) throws Exception {
+            Baz baz = new Baz();
+            baz.value = obj;
+            return baz;
+        }
+
+    }
+
     public static class Dummy2 {
         public long value;
     }
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java
new file mode 100644
index 0000000..17eeab2
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/DefaultPropertyVisibilityStrategyTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.jsonb;
+
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.annotation.JsonbProperty;
+import javax.json.bind.annotation.JsonbVisibility;
+
+import org.junit.Test;
+
+public class DefaultPropertyVisibilityStrategyTest {
+    @Test // note it is not a valid case since our default impl is internal but it guarantees we dont wrongly impl equals
+    public void subclassing() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create()) {
+            final TheClass theClass = jsonb.fromJson("{\"foo\":true}", TheClass.class);
+            assertTrue(theClass.isFoo());
+        }
+    }
+
+    @Test
+    public void annotated() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create()) {
+            final VisibleCauseAnnotated theClass = jsonb.fromJson("{\"foo\":true}", VisibleCauseAnnotated.class);
+            assertTrue(theClass.isFoo());
+        }
+    }
+
+    public static class MyVisibility extends DefaultPropertyVisibilityStrategy {
+        @Override
+        public boolean isVisible(final Field field) {
+            return true;
+        }
+
+        @Override
+        public boolean isVisible(final Method method) {
+            return true;
+        }
+    }
+
+    @JsonbVisibility(MyVisibility.class)
+    public static final class TheClass {
+        @JsonbProperty
+        private boolean foo;
+
+        public final boolean isFoo() {
+            return foo;
+        }
+    }
+
+    public static final class VisibleCauseAnnotated {
+        @JsonbProperty
+        private boolean foo;
+
+        public final boolean isFoo() {
+            return foo;
+        }
+    }
+}
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericAdapterTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericAdapterTest.java
index 6e96ca6..730e27c 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericAdapterTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/GenericAdapterTest.java
@@ -1,12 +1,10 @@
 /**
- * Copyright (C) 2006-2019 Talend Inc. - www.talend.com
- * <p>
  * Licensed 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
- * <p>
+ *
  * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
+ *
  * 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.
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/jaxrs/JsonbJaxRsTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/jaxrs/JsonbJaxRsTest.java
index 91fca7d..890f7ab 100644
--- a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/jaxrs/JsonbJaxRsTest.java
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/jaxrs/JsonbJaxRsTest.java
@@ -168,7 +168,7 @@
                 return null;
             }
         };
-        final List<Johnzon> johnzons = client().path("johnzon/all2").get(new GenericType<List<Johnzon>>(list));
+        final List<Johnzon> johnzons = client().path("johnzon/all2").get(new GenericType<List<Johnzon>>(list) {});
         assertEquals(2, johnzons.size());
         int i = 1;
         for (final Johnzon f : johnzons) {
diff --git a/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/DeserializationContextTest.java b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/DeserializationContextTest.java
new file mode 100644
index 0000000..1be5e35
--- /dev/null
+++ b/johnzon-jsonb/src/test/java/org/apache/johnzon/jsonb/serializer/DeserializationContextTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.johnzon.jsonb.serializer;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.Type;
+
+import javax.json.bind.Jsonb;
+import javax.json.bind.JsonbBuilder;
+import javax.json.bind.JsonbConfig;
+import javax.json.bind.serializer.DeserializationContext;
+import javax.json.bind.serializer.JsonbDeserializer;
+import javax.json.stream.JsonParser;
+
+import org.junit.Test;
+
+public class DeserializationContextTest {
+
+    /**
+     * Test Case for <a href="https://issues.apache.org/jira/browse/JOHNZON-277">JOHNZON-277</a>
+     *
+     * According to https://github.com/eclipse-ee4j/jsonb-api/blob/master/api/src/main/java/javax/json/bind/serializer/DeserializationContext.java#L34-L35 a
+     * deserialization context must be able to deserialize an object EVEN IN CASE the parser cursor is at {@code START_OBJECT} or {@code START_ARRAY}.
+     *
+     * Quote: "JsonParser cursor have to be at KEY_NAME before START_OBJECT / START_ARRAY, or at START_OBJECT / START_ARRAY 35 * to call this method."
+     *
+     * The latter case, "...or at START_OBJECT..." seems to fail in case the inner JSON to parse is an empty block "{}" (also for empty arrays).
+     */
+    @Test
+    public void issue277() throws Exception {
+        try (final Jsonb jsonb = JsonbBuilder.create()) {
+            // CAN create Bar and Bar[] instance from empty JSON inside of Foo
+            final Foo foo = jsonb.fromJson("{\"bar\":{},\"bars\":[]}", Foo.class);
+            assertNotNull(foo);
+            assertNotNull(foo.bar);
+            assertNotNull(foo.bars);
+        }
+
+        try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withDeserializers(new CustomDeserializer()))) {
+            // CANNOT create Bar nor Bar[] instance from empty JSON when parser cursor is at START_OBJECT => Bug in Johnzon
+            final Foo foo = jsonb.fromJson("{\"bar\":{},\"bars\":[]}", Foo.class); // throws exception "Unknown structure: END_OBJECT / END_ARRAY"
+            assertNotNull(foo);
+            assertNotNull(foo.bar);
+            assertNotNull(foo.bars);
+        }
+    }
+
+    public static class CustomDeserializer implements JsonbDeserializer<Foo> {
+        @Override
+        public Foo deserialize(final JsonParser parser, final DeserializationContext ctx, final Type rtType) {
+            parser.next(); // now cursor is at START_OBJECT of outer Foo, so let's create empty Foo...
+            final Foo foo = new Foo();
+            parser.next(); // now cursor is at KEY_NAME "bar"
+            parser.next(); // now cursor is at START_OBJECT of inner Bar, so let's fill Foo.bar...
+            foo.bar = ctx.deserialize(Bar.class, parser);
+            parser.next(); // now cursor is at KEY_NAME "bars"
+            parser.next(); // now cursor is at START_ARRAY of inner Bar[], so let's fill Foo.bars...
+            foo.bars = ctx.deserialize(Bar[].class, parser);
+            return foo;
+        }
+    }
+
+    public static class Foo {
+        public Bar bar;
+        public Bar[] bars;
+    }
+
+    public static class Bar {
+        // intentionally left blank
+    }
+
+}
diff --git a/johnzon-jsonschema/pom.xml b/johnzon-jsonschema/pom.xml
index 744708f..6bc83dc 100644
--- a/johnzon-jsonschema/pom.xml
+++ b/johnzon-jsonschema/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/johnzon-mapper/pom.xml b/johnzon-mapper/pom.xml
index 8c5f75f..ccd086c 100644
--- a/johnzon-mapper/pom.xml
+++ b/johnzon-mapper/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
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 b05667c..6eadc0e 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
@@ -22,7 +22,7 @@
 import static java.util.Collections.emptyMap;
 import static java.util.Locale.ROOT;
 
-import org.apache.johnzon.core.JsonParserFactoryImpl;
+// import org.apache.johnzon.core.JsonParserFactoryImpl; // don't depend on core in mapper
 import org.apache.johnzon.mapper.access.AccessMode;
 import org.apache.johnzon.mapper.access.BaseAccessMode;
 import org.apache.johnzon.mapper.access.FieldAccessMode;
@@ -158,7 +158,7 @@
             }
             final Map<String, Object> config = new HashMap<String, Object>();
             if (bufferStrategy != null) {
-                config.put(JsonParserFactoryImpl.BUFFER_STRATEGY, bufferStrategy);
+                config.put("org.apache.johnzon.buffer-strategy", bufferStrategy);
             }
             if (pretty) {
                 config.put(JsonGenerator.PRETTY_PRINTING, true);
@@ -170,17 +170,20 @@
 
             config.remove(JsonGenerator.PRETTY_PRINTING); // doesnt mean anything anymore for reader
             if (supportsComments) {
-                config.put(JsonParserFactoryImpl.SUPPORTS_COMMENTS, "true");
+                config.put("org.apache.johnzon.supports-comments", "true");
             }
             if (maxSize > 0) {
-                config.put(JsonParserFactoryImpl.MAX_STRING_LENGTH, maxSize);
+                config.put("org.apache.johnzon.max-string-length", maxSize);
             }
             if (bufferSize > 0) {
-                config.put(JsonParserFactoryImpl.BUFFER_LENGTH, bufferSize);
+                config.put("org.apache.johnzon.default-char-buffer", bufferSize);
             }
             if (autoAdjustStringBuffers) {
                 config.put("org.apache.johnzon.auto-adjust-buffer", true);
             }
+            if (encoding != null) {
+                config.put("org.apache.johnzon.encoding", encoding.name());
+            }
             if (readerFactory == null) {
                 readerFactory = provider.createReaderFactory(config);
             }
@@ -436,7 +439,7 @@
     }
 
     public MapperBuilder setEncoding(final String encoding) {
-        this.encoding = Charset.forName(encoding);
+        this.encoding = encoding == null ? null : Charset.forName(encoding);
         return this;
     }
 
diff --git a/johnzon-maven-plugin/.gitattributes b/johnzon-maven-plugin/.gitattributes
new file mode 100644
index 0000000..feb3703
--- /dev/null
+++ b/johnzon-maven-plugin/.gitattributes
@@ -0,0 +1,16 @@
+#  Copyright (C) 2019 Markus KARG
+#
+#  Licensed 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.
+
+src/test/resources/SomeValue.java text eol=lf
+
diff --git a/johnzon-maven-plugin/pom.xml b/johnzon-maven-plugin/pom.xml
index 8482b6e..66cc760 100644
--- a/johnzon-maven-plugin/pom.xml
+++ b/johnzon-maven-plugin/pom.xml
@@ -23,7 +23,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>johnzon-maven-plugin</artifactId>
diff --git a/johnzon-websocket/pom.xml b/johnzon-websocket/pom.xml
index 52c87d5..a58be30 100644
--- a/johnzon-websocket/pom.xml
+++ b/johnzon-websocket/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <artifactId>johnzon</artifactId>
     <groupId>org.apache.johnzon</groupId>
-    <version>1.2.1-SNAPSHOT</version>
+    <version>1.2.2-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
 
diff --git a/pom.xml b/pom.xml
index 2b3a152..6852af1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,14 +29,14 @@
   <groupId>org.apache.johnzon</groupId>
   <artifactId>johnzon</artifactId>
   <packaging>pom</packaging>
-  <version>1.2.1-SNAPSHOT</version>
+  <version>1.2.2-SNAPSHOT</version>
   <name>Apache Johnzon</name>
   <description>Apache Johnzon is an implementation of JSR-353 (JavaTM API for JSON Processing).</description>
   <inceptionYear>2014</inceptionYear>
   <url>http://johnzon.apache.org</url>
 
   <properties>
-    <geronimo-jsonp.version>1.2</geronimo-jsonp.version>
+    <geronimo-jsonp.version>1.3</geronimo-jsonp.version>
     <geronimo-jsonb.version>1.2</geronimo-jsonb.version>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <johnzon.site.url>https://svn.apache.org/repos/asf/johnzon/site/publish/</johnzon.site.url>
diff --git a/src/site/markdown/download.md b/src/site/markdown/download.md
index 1ecb136..a206128 100644
--- a/src/site/markdown/download.md
+++ b/src/site/markdown/download.md
@@ -41,16 +41,16 @@
 #### Binaries
 The binary distribution contains all Johnzon modules.
 
-* [apache-johnzon-1.1.12-bin.zip](https://www.apache.org/dyn/closer.lua/johnzon/johnzon-1.1.12/apache-johnzon-1.1.12-bin.zip)
-* [apache-johnzon-1.1.12-bin.zip.sha1](https://www.apache.org/dist/johnzon/johnzon-1.1.12/apache-johnzon-1.1.12-bin.zip.sha1)
-* [apache-johnzon-1.1.12-bin.zip.asc](https://www.apache.org/dist/johnzon/johnzon-1.1.12/apache-johnzon-1.1.12-bin.zip.asc)
+* [apache-johnzon-1.2.0-bin.zip](https://www.apache.org/dyn/closer.lua/johnzon/johnzon-1.2.0/apache-johnzon-1.2.0-bin.zip)
+* [apache-johnzon-1.2.0-bin.zip.sha1](https://www.apache.org/dist/johnzon/johnzon-1.2.0/apache-johnzon-1.2.0-bin.zip.sha1)
+* [apache-johnzon-1.2.0-bin.zip.asc](https://www.apache.org/dist/johnzon/johnzon-1.2.0/apache-johnzon-1.2.0-bin.zip.asc)
 
 #### Source
 Should you want to build any of the above binaries, this source bundle is the right one and covers them all.
 
-* [johnzon-1.1.12-source-release.zip](https://www.apache.org/dyn/closer.lua/johnzon/johnzon-1.1.12/johnzon-1.1.12-source-release.zip)
-* [johnzon-1.1.12-source-release.zip.sha1](https://www.apache.org/dist/johnzon/johnzon-1.1.12/johnzon-1.1.12-source-release.zip.sha1)
-* [johnzon-1.1.12-source-release.zip.asc](https://www.apache.org/dist/johnzon/johnzon-1.1.12/johnzon-1.1.12-source-release.zip.asc)
+* [johnzon-1.2.0-source-release.zip](https://www.apache.org/dyn/closer.lua/johnzon/johnzon-1.2.0/johnzon-1.2.0-source-release.zip)
+* [johnzon-1.2.0-source-release.zip.sha1](https://www.apache.org/dist/johnzon/johnzon-1.2.0/johnzon-1.2.0-source-release.zip.sha1)
+* [johnzon-1.2.0-source-release.zip.asc](https://www.apache.org/dist/johnzon/johnzon-1.2.0/johnzon-1.2.0-source-release.zip.asc)
 
 
 ## Johnzon-1.0.x
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index 0170515..91f3f9f 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -251,6 +251,8 @@
 
 Tip: ConfigurableJohnzonProvider maps most of MapperBuilder configuration letting you configure it through any IoC including not programming language based formats.
 
+IMPORTANT: when used with `johnzon-core`, `NoContentException` is not thrown in case of an empty incoming input stream by these providers except `JsrProvider` to limit the breaking changes.
+
 ### TomEE Configuration
 
 TomEE uses by default Johnzon as JAX-RS provider for versions 7.x. If you want however to customize it you need to follow this procedure:
@@ -315,6 +317,13 @@
 
 TIP: more in JohnzonBuilder class.
 
+A JAX-RS provider based on JSON-B is provided in the module as well. It is `org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider`.
+
+IMPORTANT: in JAX-RS 1.0 the provider can throw any exception he wants for an empty incoming stream on reader side. This had been broken in JAX-RS 2.x where it must throw a `javax.ws.rs.core.NoContentException`.
+To ensure you can pick the implementation you can and limit the breaking changes, you can set ̀throwNoContentExceptionOnEmptyStreams` on the provider to switch between both behaviors.
+Default will be picked from the current available API. Finally, this behavior only works with `johnzon-core`.
+
+
 #### Integration with `JsonValue`
 
 You can use some optimization to map a `JsonObject` to a POJO using Johnzon `JsonValueReader` and `JsonValueWriter`: