From f0e0106dc24663294fdc9d787981c118c2236d99 Mon Sep 17 00:00:00 2001
From: Maxim Nesen <maxim.nesen@oracle.com>
Date: Fri, 26 Feb 2021 13:50:07 +0100
Subject: [PATCH 1/9] processing order for Jackson/Jaxb annotations

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
---
 .../DefaultJacksonJaxbJsonProvider.java       | 25 ++++++++++++-------
 .../jackson/internal/model/ServiceTest.java   |  4 ++-
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
index 8b4ce1ce9f..8219cb1111 100644
--- a/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
+++ b/media/json-jackson/src/main/java/org/glassfish/jersey/jackson/internal/DefaultJacksonJaxbJsonProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -17,10 +17,11 @@
 package org.glassfish.jersey.jackson.internal;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.Module;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.cfg.Annotations;
 import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
 
-import java.util.Objects;
+import java.util.List;
 import javax.inject.Singleton;
 
 /**
@@ -29,7 +30,11 @@
 @Singleton
 public class DefaultJacksonJaxbJsonProvider extends JacksonJaxbJsonProvider {
 
+    //do not register JaxbAnnotationModule because it brakes default annotations processing
+    private static final String EXCLUDE_MODULE_NAME = "JaxbAnnotationModule";
+
     public DefaultJacksonJaxbJsonProvider() {
+        super();
         findAndRegisterModules();
     }
 
@@ -39,14 +44,16 @@ public DefaultJacksonJaxbJsonProvider(final Annotations... annotationsToUse) {
     }
 
     private void findAndRegisterModules() {
-        final ObjectMapper defaultMapper = _mapperConfig.getDefaultMapper();
-        if (Objects.nonNull(defaultMapper)) {
-            defaultMapper.findAndRegisterModules();
-        }
 
+        final ObjectMapper defaultMapper = _mapperConfig.getDefaultMapper();
         final ObjectMapper mapper = _mapperConfig.getConfiguredMapper();
-        if (Objects.nonNull(mapper)) {
-            mapper.findAndRegisterModules();
+
+        final List<Module> modules = ObjectMapper.findModules();
+        modules.removeIf(mod -> mod.getModuleName().contains(EXCLUDE_MODULE_NAME));
+
+        defaultMapper.registerModules(modules);
+        if (mapper != null) {
+            mapper.registerModules(modules);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java
index 027064b27b..f3a664cc06 100644
--- a/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java
+++ b/media/json-jackson/src/test/java/org/glassfish/jersey/jackson/internal/model/ServiceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -23,6 +23,7 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.xml.bind.annotation.XmlElement;
 
 @Path("/entity/")
 public final class ServiceTest {
@@ -45,6 +46,7 @@ private static final class EntityTest {
             this.value = value;
         }
 
+        @XmlElement(name = "jaxb")
         @JsonGetter("name")
         public final String getName() {
             return name;

From 6b87643f8b974af6bd600e4d58a026c3a0f5e33d Mon Sep 17 00:00:00 2001
From: Denis Kurochkin <dkurochkin@smartling.com>
Date: Wed, 31 Mar 2021 15:36:06 +0300
Subject: [PATCH 2/9] Proper handling of chunked input streams in
 LoggingInterceptor (#4753)

Signed-off-by: Denis Kurochkin <d.k.brazz@gmail.com>
---
 core-common/pom.xml                           |  7 +-
 .../jersey/logging/LoggingInterceptor.java    | 13 +++-
 .../logging/LoggingInterceptorTest.java       | 78 ++++++++++++++++++-
 .../src/test/resources/surefire.policy        |  5 +-
 4 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/core-common/pom.xml b/core-common/pom.xml
index 3288c5f45b..e36288f6c0 100644
--- a/core-common/pom.xml
+++ b/core-common/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -210,6 +210,11 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.hamcrest</groupId>
             <artifactId>hamcrest-library</artifactId>
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
index c90d8b6470..7ad26ec5d6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -193,7 +193,16 @@ InputStream logInboundEntity(final StringBuilder b, InputStream stream, final Ch
         }
         stream.mark(maxEntitySize + 1);
         final byte[] entity = new byte[maxEntitySize + 1];
-        final int entitySize = stream.read(entity);
+
+        int entitySize = 0;
+        while (entitySize < entity.length) {
+            int readBytes = stream.read(entity, entitySize, entity.length - entitySize);
+            if (readBytes < 0) {
+                break;
+            }
+            entitySize += readBytes;
+        }
+
         b.append(new String(entity, 0, Math.min(entitySize, maxEntitySize), charset));
         if (entitySize > maxEntitySize) {
             b.append("...more...");
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
index e4e5e91480..55162737e4 100644
--- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,14 +16,27 @@
 
 package org.glassfish.jersey.logging;
 
+import org.mockito.stubbing.Answer;
+
 import javax.ws.rs.core.MediaType;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Random;
 
 import org.junit.Test;
 import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY;
 import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY;
 import static org.glassfish.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_TEXT;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM_TYPE;
 import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE;
@@ -106,4 +119,67 @@ public void testVerbosityHeadersPrintBinaryEntity() {
         assertFalse(LoggingInterceptor.printEntity(HEADERS_ONLY, APPLICATION_OCTET_STREAM_TYPE));
     }
 
+    //
+    // logInboundEntity
+    //
+
+    @Test
+    public void testLogInboundEntityMockedStream() throws Exception {
+        int maxEntitySize = 20;
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+
+        StringBuilder buffer = new StringBuilder();
+        InputStream stream = mock(InputStream.class);
+        when(stream.markSupported()).thenReturn(true);
+
+        when(stream.read(any(), eq(0), eq(maxEntitySize + 1)))
+                .thenAnswer(chunk(4, 'a'));
+        when(stream.read(any(), eq(4), eq(maxEntitySize + 1 - 4)))
+                .thenAnswer(chunk(3, 'b'));
+        when(stream.read(any(), eq(7), eq(maxEntitySize + 1 - 7)))
+                .thenAnswer(chunk(5, 'c'));
+        when(stream.read(any(), eq(12), eq(maxEntitySize + 1 - 12)))
+                .thenReturn(-1);
+
+        loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);
+
+        assertEquals("aaaabbbccccc\n", buffer.toString());
+        verify(stream).mark(maxEntitySize + 1);
+        verify(stream).reset();
+    }
+
+    private Answer<?> chunk(int size, char filler) {
+        return invocation -> {
+            byte[] buf = invocation.getArgumentAt(0, byte[].class);
+            int offset = invocation.getArgumentAt(1, Integer.class);
+            Arrays.fill(buf, offset, offset + size, (byte) filler);
+            return size;
+        };
+    }
+
+    @Test
+    public void testLogInboundEntityRealStream() throws Exception {
+        int maxEntitySize = 2000;
+        String inputString = getRandomString(maxEntitySize * 2);
+
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+        StringBuilder buffer = new StringBuilder();
+        InputStream stream = new ByteArrayInputStream(inputString.getBytes());
+
+        loggingInterceptor.logInboundEntity(buffer, stream, StandardCharsets.UTF_8);
+
+        assertEquals(inputString.substring(0, maxEntitySize) + "...more...\n", buffer.toString());
+    }
+
+    private static String getRandomString(int length) {
+        final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 _";
+        StringBuilder result = new StringBuilder();
+
+        while (length > 0) {
+            Random rand = new Random();
+            result.append(characters.charAt(rand.nextInt(characters.length())));
+            length--;
+        }
+        return result.toString();
+    }
 }
diff --git a/core-common/src/test/resources/surefire.policy b/core-common/src/test/resources/surefire.policy
index 27602ae4c0..530db3cf62 100644
--- a/core-common/src/test/resources/surefire.policy
+++ b/core-common/src/test/resources/surefire.policy
@@ -29,12 +29,15 @@ grant codebase "file:${settings.localRepository}/-" {
 grant codebase "file:${project.build.directory}/test-classes/-" {
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
   permission java.lang.RuntimePermission "modifyThread";
-  permission java.util.PropertyPermission "*", "write";
+  permission java.util.PropertyPermission "*", "read,write";
   permission java.io.FilePermission "${java.io.tmpdir}/-", "read,write,delete";
   permission java.lang.RuntimePermission "getClassLoader";
   permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
   permission java.lang.RuntimePermission "accessClassInPackage.sun.misc.*";
   permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+  permission java.lang.RuntimePermission "accessDeclaredMembers";
+  permission java.lang.RuntimePermission "accessClassInPackage.sun.reflect";
+  permission java.lang.RuntimePermission "reflectionFactoryAccess";
 };
 
 grant codebase "file:${project.build.directory}/classes/-" {

From 0d58cf900aab0a34a424f5b88a8874df45601082 Mon Sep 17 00:00:00 2001
From: Maxim Nesen <24524084+senivam@users.noreply.github.com>
Date: Mon, 12 Apr 2021 10:21:06 +0200
Subject: [PATCH 3/9] Logging delimiter parametrized (#4745)

Signed-off-by: Maxim Nesen <maxim.nesen@oracle.com>
---
 .../jersey/logging/ClientLoggingFilter.java   |  18 +-
 .../jersey/logging/LoggingFeature.java        | 166 ++++++++++++++----
 .../LoggingFeatureAutoDiscoverable.java       |  14 +-
 .../jersey/logging/LoggingInterceptor.java    |  39 ++--
 .../jersey/logging/ServerLoggingFilter.java   |  18 +-
 .../logging/LoggingInterceptorTest.java       |   4 +-
 docs/src/main/docbook/appendix-properties.xml |  33 ++++
 docs/src/main/docbook/jersey.ent              |  10 +-
 .../tests/e2e/common/LoggingFeatureTest.java  |  41 ++++-
 9 files changed, 264 insertions(+), 79 deletions(-)

diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
index 1cd5cdb86e..07c3dbeca6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/ClientLoggingFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -53,18 +53,20 @@
 final class ClientLoggingFilter extends LoggingInterceptor implements ClientRequestFilter, ClientResponseFilter {
 
     /**
-     * Create a logging filter with custom logger and custom settings of entity
+     * Create a logging filter using builder instance with custom logger and custom settings of entity
      * logging.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    public ClientLoggingFilter(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        super(logger, level, verbosity, maxEntitySize);
+    public ClientLoggingFilter(LoggingFeature.LoggingFeatureBuilder builder) {
+        super(builder);
     }
 
     @Override
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
index e4e2b01f03..e6ed421d5f 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeature.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -40,6 +40,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR}</li>
  * </ul>
  * <p>
  * If any of the configuration value is not set, following default values are applied:
@@ -56,6 +57,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL_SERVER}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY_SERVER}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR_SERVER}</li>
  * </ul>
  * Client configurable properties:
  * <ul>
@@ -63,6 +65,7 @@
  * <li>{@link #LOGGING_FEATURE_LOGGER_LEVEL_CLIENT}</li>
  * <li>{@link #LOGGING_FEATURE_VERBOSITY_CLIENT}</li>
  * <li>{@link #LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT}</li>
+ * <li>{@link #LOGGING_FEATURE_SEPARATOR_CLIENT}</li>
  * </ul>
  *
  * @author Ondrej Kosatka
@@ -86,11 +89,16 @@ public class LoggingFeature implements Feature {
      * Default verbosity for entity logging. See {@link Verbosity}.
      */
     public static final Verbosity DEFAULT_VERBOSITY = Verbosity.PAYLOAD_TEXT;
+    /**
+     * Default separator for entity logging.
+     */
+    public static final String DEFAULT_SEPARATOR = "\n";
 
     private static final String LOGGER_NAME_POSTFIX = ".logger.name";
     private static final String LOGGER_LEVEL_POSTFIX = ".logger.level";
     private static final String VERBOSITY_POSTFIX = ".verbosity";
     private static final String MAX_ENTITY_POSTFIX = ".entity.maxSize";
+    private static final String SEPARATOR_POSTFIX = ".separator";
     private static final String LOGGING_FEATURE_COMMON_PREFIX = "jersey.config.logging";
     /**
      * Common logger name property.
@@ -108,6 +116,10 @@ public class LoggingFeature implements Feature {
      * Common property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE = LOGGING_FEATURE_COMMON_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Common property for configuring logging separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR = LOGGING_FEATURE_COMMON_PREFIX + SEPARATOR_POSTFIX;
 
     private static final String LOGGING_FEATURE_SERVER_PREFIX = "jersey.config.server.logging";
     /**
@@ -126,6 +138,10 @@ public class LoggingFeature implements Feature {
      * Server property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER = LOGGING_FEATURE_SERVER_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Server property for configuring separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR_SERVER = LOGGING_FEATURE_SERVER_PREFIX + SEPARATOR_POSTFIX;
 
     private static final String LOGGING_FEATURE_CLIENT_PREFIX = "jersey.config.client.logging";
     /**
@@ -144,11 +160,12 @@ public class LoggingFeature implements Feature {
      * Client property for configuring a maximum number of bytes of entity to be logged.
      */
     public static final String LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT = LOGGING_FEATURE_CLIENT_PREFIX + MAX_ENTITY_POSTFIX;
+    /**
+     * Client property for logging separator.
+     */
+    public static final String LOGGING_FEATURE_SEPARATOR_CLIENT = LOGGING_FEATURE_CLIENT_PREFIX + SEPARATOR_POSTFIX;
 
-    private final Logger filterLogger;
-    private final Verbosity verbosity;
-    private final Integer maxEntitySize;
-    private final Level level;
+    private final LoggingFeatureBuilder builder;
 
     /**
      * Creates the feature with default values.
@@ -199,32 +216,62 @@ public LoggingFeature(Logger logger, Integer maxEntitySize) {
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
      */
     public LoggingFeature(Logger logger, Level level, Verbosity verbosity, Integer maxEntitySize) {
-        this.filterLogger = logger;
-        this.level = level;
-        this.verbosity = verbosity;
-        this.maxEntitySize = maxEntitySize;
+
+        this(LoggingFeature.builder()
+                .withLogger(logger)
+                .level(level)
+                .verbosity(verbosity)
+                .maxEntitySize(maxEntitySize)
+                .separator(DEFAULT_SEPARATOR)
+        );
+
+    }
+
+    /**
+     * Constructor based on logging feature builder. All parameters are passed through a builder instance.
+     *
+     * @param builder instance of a builder with required logging feature parameters
+     */
+    public LoggingFeature(LoggingFeatureBuilder builder) {
+        this.builder = builder;
     }
 
     @Override
     public boolean configure(FeatureContext context) {
-        boolean enabled = false;
+        boolean enabled = context.getConfiguration().getRuntimeType() != null;
 
-        if (context.getConfiguration().getRuntimeType() == RuntimeType.CLIENT) {
-            ClientLoggingFilter clientLoggingFilter = (ClientLoggingFilter) createLoggingFilter(context, RuntimeType.CLIENT);
-            context.register(clientLoggingFilter);
-            enabled = true;
-        }
-        if (context.getConfiguration().getRuntimeType() == RuntimeType.SERVER) {
-            ServerLoggingFilter serverClientFilter = (ServerLoggingFilter) createLoggingFilter(context, RuntimeType.SERVER);
-            context.register(serverClientFilter);
-            enabled = true;
+        if (enabled) {
+            context.register(createLoggingFilter(context, context.getConfiguration().getRuntimeType()));
         }
+
         return enabled;
     }
 
+    /**
+     * builder method to create  LoggingFeature with required settings
+     *
+     * @return Builder for LoggingFeature
+     */
+    public static LoggingFeatureBuilder builder() {
+        return new LoggingFeatureBuilder();
+    }
+
     private LoggingInterceptor createLoggingFilter(FeatureContext context, RuntimeType runtimeType) {
-        Map properties = context.getConfiguration().getProperties();
-        String filterLoggerName = CommonProperties.getValue(
+
+        final LoggingFeatureBuilder loggingBuilder =
+                configureBuilderParameters(builder, context, runtimeType);
+
+        return (runtimeType == RuntimeType.SERVER)
+                ? new ServerLoggingFilter(loggingBuilder)
+                : new ClientLoggingFilter(loggingBuilder);
+    }
+
+    private static LoggingFeatureBuilder configureBuilderParameters(LoggingFeatureBuilder builder,
+                                                   FeatureContext context, RuntimeType runtimeType) {
+
+        final Map properties = context.getConfiguration().getProperties();
+        //get values from properties (if any)
+        final String filterLoggerName = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_LOGGER_NAME_SERVER : LOGGING_FEATURE_LOGGER_NAME_CLIENT,
                 CommonProperties.getValue(
@@ -232,14 +279,21 @@ private LoggingInterceptor createLoggingFilter(FeatureContext context, RuntimeTy
                         LOGGING_FEATURE_LOGGER_NAME,
                         DEFAULT_LOGGER_NAME
                 ));
-        String filterLevel = CommonProperties.getValue(
+        final String filterLevel = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_LOGGER_LEVEL_SERVER : LOGGING_FEATURE_LOGGER_LEVEL_CLIENT,
                 CommonProperties.getValue(
                         context.getConfiguration().getProperties(),
                         LOGGING_FEATURE_LOGGER_LEVEL,
                         DEFAULT_LOGGER_LEVEL));
-        Verbosity filterVerbosity = CommonProperties.getValue(
+        final String filterSeparator = CommonProperties.getValue(
+                properties,
+                runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_SEPARATOR_SERVER : LOGGING_FEATURE_SEPARATOR_CLIENT,
+                CommonProperties.getValue(
+                        context.getConfiguration().getProperties(),
+                        LOGGING_FEATURE_SEPARATOR,
+                        DEFAULT_SEPARATOR));
+        final Verbosity filterVerbosity = CommonProperties.getValue(
                 properties,
                 runtimeType == RuntimeType.SERVER ? LOGGING_FEATURE_VERBOSITY_SERVER : LOGGING_FEATURE_VERBOSITY_CLIENT,
                 CommonProperties.getValue(
@@ -257,19 +311,16 @@ private LoggingInterceptor createLoggingFilter(FeatureContext context, RuntimeTy
                         DEFAULT_MAX_ENTITY_SIZE
                 ));
 
-        Level loggerLevel = Level.parse(filterLevel);
-
-        if (runtimeType == RuntimeType.SERVER) {
-            return new ServerLoggingFilter(filterLogger != null ? filterLogger : Logger.getLogger(filterLoggerName),
-                    level != null ? level : loggerLevel,
-                    verbosity != null ? verbosity : filterVerbosity,
-                    maxEntitySize != null ? maxEntitySize : filterMaxEntitySize);
-        } else {
-            return new ClientLoggingFilter(filterLogger != null ? filterLogger : Logger.getLogger(filterLoggerName),
-                    level != null ? level : loggerLevel,
-                    verbosity != null ? verbosity : filterVerbosity,
-                    maxEntitySize != null ? maxEntitySize : filterMaxEntitySize);
-        }
+        final Level loggerLevel = Level.parse(filterLevel);
+
+        //configure builder vs properties values
+        builder.filterLogger = builder.filterLogger == null ? Logger.getLogger(filterLoggerName) : builder.filterLogger;
+        builder.verbosity = builder.verbosity == null ? filterVerbosity : builder.verbosity;
+        builder.maxEntitySize = builder.maxEntitySize == null ? filterMaxEntitySize : builder.maxEntitySize;
+        builder.level = builder.level == null ? loggerLevel : builder.level;
+        builder.separator = builder.separator == null ? filterSeparator : builder.separator;
+
+        return builder;
     }
 
     /**
@@ -313,4 +364,45 @@ public enum Verbosity {
          */
         PAYLOAD_ANY
     }
-}
+
+    /**
+     * Builder class for logging feature configuration. Accepts parameters for the filter logger, verbosity, max
+     * entity size, level, and separator.
+     */
+    public static class LoggingFeatureBuilder {
+
+        Logger filterLogger;
+        Verbosity verbosity;
+        Integer maxEntitySize;
+        Level level;
+        String separator;
+
+        public LoggingFeatureBuilder() {
+
+        }
+        public LoggingFeatureBuilder withLogger(Logger logger) {
+            this.filterLogger = logger;
+            return this;
+        }
+        public LoggingFeatureBuilder verbosity(Verbosity verbosity) {
+            this.verbosity = verbosity;
+            return this;
+        }
+        public LoggingFeatureBuilder maxEntitySize(Integer maxEntitySize) {
+            this.maxEntitySize = maxEntitySize;
+            return this;
+        }
+        public LoggingFeatureBuilder level(Level level) {
+            this.level = level;
+            return this;
+        }
+        public LoggingFeatureBuilder separator(String separator) {
+            this.separator = separator;
+            return this;
+        }
+
+        public LoggingFeature build() {
+            return new LoggingFeature(this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
index 205ed08b5f..01e61a5e9d 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingFeatureAutoDiscoverable.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -34,6 +34,9 @@
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT;
+import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT;
 import static org.glassfish.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER;
@@ -71,20 +74,23 @@ private boolean commonPropertyConfigured(Map properties) {
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR);
     }
 
     private boolean clientConfigured(Map properties) {
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME_CLIENT)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL_CLIENT)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY_CLIENT)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR_CLIENT);
     }
 
     private boolean serverConfigured(Map properties) {
         return properties.containsKey(LOGGING_FEATURE_LOGGER_NAME_SERVER)
                 || properties.containsKey(LOGGING_FEATURE_LOGGER_LEVEL_SERVER)
                 || properties.containsKey(LOGGING_FEATURE_VERBOSITY_SERVER)
-                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER);
+                || properties.containsKey(LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER)
+                || properties.containsKey(LOGGING_FEATURE_SEPARATOR_SERVER);
     }
 }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
index 7ad26ec5d6..601d6b2828 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/LoggingInterceptor.java
@@ -103,23 +103,28 @@ public int compare(final Map.Entry<String, List<String>> o1, final Map.Entry<Str
     final AtomicLong _id = new AtomicLong(0);
     final Verbosity verbosity;
     final int maxEntitySize;
+    final String separator;
 
     /**
-     * Creates a logging filter with custom logger and entity logging turned on, but potentially limiting the size
-     * of entity to be buffered and logged.
+     * Creates a logging filter using builder instance with custom logger and entity logging turned on,
+     * but potentially limiting the size of entity to be buffered and logged.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    LoggingInterceptor(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        this.logger = logger;
-        this.level = level;
-        this.verbosity = verbosity;
-        this.maxEntitySize = Math.max(0, maxEntitySize);
+
+    LoggingInterceptor(LoggingFeature.LoggingFeatureBuilder builder) {
+        this.logger = builder.filterLogger;
+        this.level = builder.level;
+        this.verbosity = builder.verbosity;
+        this.maxEntitySize = Math.max(0, builder.maxEntitySize);
+        this.separator = builder.separator;
     }
 
     /**
@@ -142,18 +147,18 @@ void printRequestLine(final StringBuilder b, final String note, final long id, f
         prefixId(b, id).append(NOTIFICATION_PREFIX)
                 .append(note)
                 .append(" on thread ").append(Thread.currentThread().getName())
-                .append("\n");
+                .append(separator);
         prefixId(b, id).append(REQUEST_PREFIX).append(method).append(" ")
-                .append(uri.toASCIIString()).append("\n");
+                .append(uri.toASCIIString()).append(separator);
     }
 
     void printResponseLine(final StringBuilder b, final String note, final long id, final int status) {
         prefixId(b, id).append(NOTIFICATION_PREFIX)
                 .append(note)
-                .append(" on thread ").append(Thread.currentThread().getName()).append("\n");
+                .append(" on thread ").append(Thread.currentThread().getName()).append(separator);
         prefixId(b, id).append(RESPONSE_PREFIX)
                 .append(Integer.toString(status))
-                .append("\n");
+                .append(separator);
     }
 
     void printPrefixedHeaders(final StringBuilder b,
@@ -165,7 +170,7 @@ void printPrefixedHeaders(final StringBuilder b,
             final String header = headerEntry.getKey();
 
             if (val.size() == 1) {
-                prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append("\n");
+                prefixId(b, id).append(prefix).append(header).append(": ").append(val.get(0)).append(separator);
             } else {
                 final StringBuilder sb = new StringBuilder();
                 boolean add = false;
@@ -176,7 +181,7 @@ void printPrefixedHeaders(final StringBuilder b,
                     add = true;
                     sb.append(s);
                 }
-                prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append("\n");
+                prefixId(b, id).append(prefix).append(header).append(": ").append(sb.toString()).append(separator);
             }
         }
     }
diff --git a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
index 8405edff43..b1c2c7b393 100644
--- a/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
+++ b/core-common/src/main/java/org/glassfish/jersey/logging/ServerLoggingFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -53,18 +53,20 @@
 final class ServerLoggingFilter extends LoggingInterceptor implements ContainerRequestFilter, ContainerResponseFilter {
 
     /**
-     * Create a logging filter with custom logger and custom settings of entity
+     * Create a logging filter using builder instance with custom logger and custom settings of entity
      * logging.
      *
-     * @param logger        the logger to log messages to.
-     * @param level         level at which the messages will be logged.
-     * @param verbosity     verbosity of the logged messages. See {@link Verbosity}.
-     * @param maxEntitySize maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
+     * @param builder       loggingFeatureBuilder which contains values for:
+     *  logger         the logger to log messages to.
+     *  level          level at which the messages will be logged.
+     *  verbosity      verbosity of the logged messages. See {@link Verbosity}.
+     *  maxEntitySize  maximum number of entity bytes to be logged (and buffered) - if the entity is larger,
      *                      logging filter will print (and buffer in memory) only the specified number of bytes
      *                      and print "...more..." string at the end. Negative values are interpreted as zero.
+     *  separator      delimiter for particular log lines. Default is Linux new line delimiter
      */
-    public ServerLoggingFilter(final Logger logger, final Level level, final Verbosity verbosity, final int maxEntitySize) {
-        super(logger, level, verbosity, maxEntitySize);
+    public ServerLoggingFilter(final LoggingFeature.LoggingFeatureBuilder builder) {
+        super(builder);
     }
 
     @Override
diff --git a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
index 55162737e4..c19cb1934e 100644
--- a/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
+++ b/core-common/src/test/java/org/glassfish/jersey/logging/LoggingInterceptorTest.java
@@ -126,7 +126,7 @@ public void testVerbosityHeadersPrintBinaryEntity() {
     @Test
     public void testLogInboundEntityMockedStream() throws Exception {
         int maxEntitySize = 20;
-        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(LoggingFeature.builder().maxEntitySize(maxEntitySize)) {};
 
         StringBuilder buffer = new StringBuilder();
         InputStream stream = mock(InputStream.class);
@@ -162,7 +162,7 @@ public void testLogInboundEntityRealStream() throws Exception {
         int maxEntitySize = 2000;
         String inputString = getRandomString(maxEntitySize * 2);
 
-        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(null, null, null, maxEntitySize) {};
+        LoggingInterceptor loggingInterceptor = new LoggingInterceptor(LoggingFeature.builder().maxEntitySize(maxEntitySize)) {};
         StringBuilder buffer = new StringBuilder();
         InputStream stream = new ByteArrayInputStream(inputString.getBytes());
 
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index 3eecfaff0c..6011f89ed5 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -143,6 +143,17 @@
                             See <link linkend="logging.xml">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            Custom logging delimiter for new lines separation.
+                            See <link linkend="logging.xml">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
@@ -584,6 +595,17 @@
                             See <link linkend="logging.xml">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.server.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            Custom delimiter for new lines separation.
+                            See <link linkend="logging.xml">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
@@ -911,6 +933,17 @@
                             See <link linkend="logging_chapter">logging</link> chapter for more information.
                         </entry>
                     </row>
+                    <row>
+                        <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT;
+                        </entry>
+                        <entry>
+                            <literal>jersey.config.client.logging.entity.separator</literal>
+                        </entry>
+                        <entry>
+                            New line delimiter property (client side).
+                            See <link linkend="logging_chapter">logging</link> chapter for more information.
+                        </entry>
+                    </row>
                 </tbody>
             </tgroup>
         </table>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 9ebf7c2d4d..fd87f28186 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -409,14 +409,17 @@
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY'>LoggingFeature.LOGGING_FEATURE_VERBOSITY</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_SEPARATOR'>LoggingFeature.LOGGING_FEATURE_SEPARATOR</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_NAME_CLIENT'>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL_CLIENT'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY_CLIENT'>LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT'>LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_NAME_SERVER'>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_LOGGER_LEVEL_SERVER'>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_VERBOSITY_SERVER'>LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER'>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER</link>">
+<!ENTITY jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.html#LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER'>LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html'>LoggingFeature.Verbosity</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html#HEADERS_ONLY'>LoggingFeature.Verbosity.HEADERS_ONLY</link>">
 <!ENTITY jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY "<link xlink:href='&jersey.javadoc.uri.prefix;/logging/LoggingFeature.Verbosity.html#PAYLOAD_ANY'>LoggingFeature.Verbosity.PAYLOAD_ANY</link>">
@@ -892,14 +895,17 @@
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATOR</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_CLIENT</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_CLIENT "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATORE_CLIENT</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_VERBOSITY_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_MAX_ENTITY_SIZE_SERVER</literal>">
+<!ENTITY lit.jersey.logging.LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER "<literal>LoggingFeature.LOGGING_FEATURE_SEPARATOR_SERVER</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity "<literal>LoggingFeature.Verbosity</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity.HEADERS_ONLY "<literal>LoggingFeature.Verbosity.HEADERS_ONLY</literal>">
 <!ENTITY lit.jersey.logging.LoggingFeature.Verbosity.PAYLOAD_ANY "<literal>LoggingFeature.Verbosity.PAYLOAD_ANY</literal>">
@@ -1044,4 +1050,4 @@
 <!ENTITY lit.jersey.test.util.LoopBackConnectorProvider "<literal>LoopBackConnectorProvider</literal>">
 <!ENTITY lit.jersey.test.util.ContainerRequestBuilder "<literal>ContainerRequestBuilder</literal>">
 
-<!ENTITY lit.jsonb.Jsonb "<literal>Jsonb</literal>">
\ No newline at end of file
+<!ENTITY lit.jsonb.Jsonb "<literal>Jsonb</literal>">
diff --git a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
index 280e94d251..b85b8dfbd1 100644
--- a/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
+++ b/tests/e2e/src/test/java/org/glassfish/jersey/tests/e2e/common/LoggingFeatureTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -76,6 +76,7 @@ public class LoggingFeatureTest {
     private static final String BINARY_MEDIA_TYPE = "application/binary";
     private static final String TEXT_MEDIA_TYPE = MediaType.TEXT_PLAIN;
     private static final String ENTITY = "This entity must (not) be logged";
+    private static final String SEPARATOR = "!-------!";
 
     @Path("/")
     public static class MyResource {
@@ -245,6 +246,44 @@ public void testPostedEntityLogged() throws Exception {
             assertThat(getLoggingFilterLogRecord(getLoggedRecords()).get(0).getMessage(), containsString(ENTITY));
         }
 
+        @Test
+        public void testLoggingFeatureBuilderSeparator() {
+            final Response response = target("/text")
+                    .register(LoggingFeature
+                            .builder()
+                            .withLogger(Logger.getLogger(LOGGER_NAME))
+                            .verbosity(LoggingFeature.Verbosity.PAYLOAD_ANY)
+                            .separator(SEPARATOR)
+                            .build()
+                    ).request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper separator.
+            final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
+            assertThat(record.getMessage(), containsString(SEPARATOR));
+
+        }
+
+        @Test
+        public void testLoggingFeatureBuilderProperty() {
+            final Response response = target("/text")
+                    .register(LoggingFeature
+                            .builder()
+                            .withLogger(Logger.getLogger(LOGGER_NAME))
+                            .build()
+                    ).property(LoggingFeature.LOGGING_FEATURE_SEPARATOR, SEPARATOR)
+                    .request()
+                    .post(Entity.text(ENTITY));
+
+            // Correct response status.
+            assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
+            // Check logs for proper separator.
+            final LogRecord record = getLoggingFilterResponseLogRecord(getLoggedRecords());
+            assertThat(record.getMessage(), containsString(SEPARATOR));
+        }
+
     }
 
     /**

From 73206d9f963014fe4be5d4ab97bd78f69792d08a Mon Sep 17 00:00:00 2001
From: jansupol <jan.supol@oracle.com>
Date: Wed, 24 Mar 2021 22:31:30 +0100
Subject: [PATCH 4/9] Adopt ASM 9.1 to support JDK 17

Signed-off-by: jansupol <jan.supol@oracle.com>
---
 .../org/objectweb/asm/AnnotationVisitor.java  |  4 ++--
 .../org/objectweb/asm/ClassReader.java        | 24 ++++++++++++++-----
 .../repackaged/org/objectweb/asm/Opcodes.java |  5 ++--
 .../scanning/AnnotationAcceptingListener.java |  4 ++--
 pom.xml                                       |  2 +-
 5 files changed, 26 insertions(+), 13 deletions(-)

diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java
index 33293bb473..8ca2039014 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/AnnotationVisitor.java
@@ -131,9 +131,9 @@ public AnnotationVisitor visitAnnotation(final String name, final String descrip
   }
 
   /**
-   * Visits an array value of the annotation. Note that arrays of primitive types (such as byte,
+   * Visits an array value of the annotation. Note that arrays of primitive values (such as byte,
    * boolean, short, char, int, long, float or double) can be passed as value to {@link #visit
-   * visit}. This is what {@link ClassReader} does.
+   * visit}. This is what {@link ClassReader} does for non empty arrays of primitive values.
    *
    * @param name the value name.
    * @return a visitor to visit the actual array value elements, or {@literal null} if this visitor
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
index 25015fdd18..a46fdad3e1 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/ClassReader.java
@@ -100,8 +100,10 @@ public class ClassReader {
   @Deprecated
   // DontCheck(MemberName): can't be renamed (for backward binary compatibility).
   public final byte[] b;
+
   /** The offset in bytes of the ClassFile's access_flags field. */
   public final int header;
+
   /**
    * A byte array containing the JVMS ClassFile structure to be parsed. <i>The content of this array
    * must not be modified. This field is intended for {@link Attribute} sub classes, and is normally
@@ -112,6 +114,7 @@ public class ClassReader {
    * ClassFile element offsets within this byte array.
    */
   final byte[] classFileBuffer;
+
   /**
    * The offset in bytes, in {@link #classFileBuffer}, of each cp_info entry of the ClassFile's
    * constant_pool array, <i>plus one</i>. In other words, the offset of constant pool entry i is
@@ -119,16 +122,19 @@ public class ClassReader {
    * 1].
    */
   private final int[] cpInfoOffsets;
+
   /**
    * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids
    * multiple parsing of a given CONSTANT_Utf8 constant pool item.
    */
   private final String[] constantUtf8Values;
+
   /**
    * The ConstantDynamic objects corresponding to the CONSTANT_Dynamic constant pool items. This
    * cache avoids multiple parsing of a given CONSTANT_Dynamic constant pool item.
    */
   private final ConstantDynamic[] constantDynamicValues;
+
   /**
    * The start offsets in {@link #classFileBuffer} of each element of the bootstrap_methods array
    * (in the BootstrapMethods attribute).
@@ -137,6 +143,7 @@ public class ClassReader {
    *     4.7.23</a>
    */
   private final int[] bootstrapMethodOffsets;
+
   /**
    * A conservative estimate of the maximum length of the strings contained in the constant pool of
    * the class.
@@ -184,7 +191,7 @@ public ClassReader(
     this.b = classFileBuffer;
     // Check the class' major_version. This field is after the magic and minor_version fields, which
     // use 4 and 2 bytes respectively.
-    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V16) {
+    if (checkClassVersion && readShort(classFileOffset + 6) > Opcodes.V17) {
       throw new IllegalArgumentException(
           "Unsupported class file major version " + readShort(classFileOffset + 6));
     }
@@ -499,6 +506,9 @@ public void accept(
       } else if (Constants.SYNTHETIC.equals(attributeName)) {
         accessFlags |= Opcodes.ACC_SYNTHETIC;
       } else if (Constants.SOURCE_DEBUG_EXTENSION.equals(attributeName)) {
+        if (attributeLength > classFileBuffer.length - currentAttributeOffset) {
+          throw new IllegalArgumentException();
+        }
         sourceDebugExtension =
             readUtf(currentAttributeOffset, attributeLength, new char[attributeLength]);
       } else if (Constants.RUNTIME_INVISIBLE_ANNOTATIONS.equals(attributeName)) {
@@ -1509,6 +1519,9 @@ private void readCode(
     final int maxLocals = readUnsignedShort(currentOffset + 2);
     final int codeLength = readInt(currentOffset + 4);
     currentOffset += 8;
+    if (codeLength > classFileBuffer.length - currentOffset) {
+      throw new IllegalArgumentException();
+    }
 
     // Read the bytecode 'code' array to create a label for each referenced instruction.
     final int bytecodeStartOffset = currentOffset;
@@ -3438,7 +3451,6 @@ final int getFirstAttributeOffset() {
   private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
     char[] charBuffer = new char[maxStringLength];
     int currentAttributeOffset = getFirstAttributeOffset();
-    int[] currentBootstrapMethodOffsets = null;
     for (int i = readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
       // Read the attribute_info's attribute_name and attribute_length fields.
       String attributeName = readUTF8(currentAttributeOffset, charBuffer);
@@ -3446,17 +3458,17 @@ private int[] readBootstrapMethodsAttribute(final int maxStringLength) {
       currentAttributeOffset += 6;
       if (Constants.BOOTSTRAP_METHODS.equals(attributeName)) {
         // Read the num_bootstrap_methods field and create an array of this size.
-        currentBootstrapMethodOffsets = new int[readUnsignedShort(currentAttributeOffset)];
+        int[] result = new int[readUnsignedShort(currentAttributeOffset)];
         // Compute and store the offset of each 'bootstrap_methods' array field entry.
         int currentBootstrapMethodOffset = currentAttributeOffset + 2;
-        for (int j = 0; j < currentBootstrapMethodOffsets.length; ++j) {
-          currentBootstrapMethodOffsets[j] = currentBootstrapMethodOffset;
+        for (int j = 0; j < result.length; ++j) {
+          result[j] = currentBootstrapMethodOffset;
           // Skip the bootstrap_method_ref and num_bootstrap_arguments fields (2 bytes each),
           // as well as the bootstrap_arguments array field (of size num_bootstrap_arguments * 2).
           currentBootstrapMethodOffset +=
               4 + readUnsignedShort(currentBootstrapMethodOffset + 2) * 2;
         }
-        return currentBootstrapMethodOffsets;
+        return result;
       }
       currentAttributeOffset += attributeLength;
     }
diff --git a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
index f9beb52256..9e9bdd770d 100644
--- a/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
+++ b/core-server/src/main/java/jersey/repackaged/org/objectweb/asm/Opcodes.java
@@ -133,7 +133,7 @@ public interface Opcodes {
    * <pre>
    * public class StuffVisitor {
    *   &#64;Deprecated public void visitOldStuff(int arg, ...) {
-   *     visitNewStuf(arg | SOURCE_DEPRECATED, ...);
+   *     visitNewStuff(arg | SOURCE_DEPRECATED, ...);
    *   }
    *   public void visitNewStuff(int argAndSource...) {
    *     if ((argAndSource & SOURCE_DEPRECATED) == 0) {
@@ -155,7 +155,7 @@ public interface Opcodes {
    * <p>and there are two cases:
    *
    * <ul>
-   *   <li>call visitOldSuff: in the call to super.visitOldStuff, the source is set to
+   *   <li>call visitOldStuff: in the call to super.visitOldStuff, the source is set to
    *       SOURCE_DEPRECATED and visitNewStuff is called. Here 'do stuff' is run because the source
    *       was previously set to SOURCE_DEPRECATED, and execution eventually returns to
    *       UserStuffVisitor.visitOldStuff, where 'do user stuff' is run.
@@ -282,6 +282,7 @@ public interface Opcodes {
   int V14 = 0 << 16 | 58;
   int V15 = 0 << 16 | 59;
   int V16 = 0 << 16 | 60;
+  int V17 = 0 << 16 | 61;
 
   /**
    * Version flag indicating that the class is using 'preview' features.
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
index 5efc6bae45..96e840c1c2 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/scanning/AnnotationAcceptingListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -303,7 +303,7 @@ private Class getClassForName(final String className) {
 
     private static class ClassReaderWrapper {
         private static final Logger LOGGER = Logger.getLogger(ClassReader.class.getName());
-        private static final int WARN_VERSION = Opcodes.V16;
+        private static final int WARN_VERSION = Opcodes.V17;
         private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096;
 
         private final byte[] b;
diff --git a/pom.xml b/pom.xml
index 9baf7e603e..bc916373b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2074,7 +2074,7 @@
         <jersey.version>${project.version}</jersey.version>
         <!-- asm is now source integrated - keeping this property to see the version -->
         <!-- see core-server/src/main/java/jersey/repackaged/asm/.. -->
-        <asm.version>9.0</asm.version>
+        <asm.version>9.1</asm.version>
         <bnd.plugin.version>2.3.6</bnd.plugin.version>
         <cdi.api.version>1.1</cdi.api.version>
         <commons-lang3.version>3.3.2</commons-lang3.version>

From d2b7b65b8f9baa8ed3069f083890badb63911dd9 Mon Sep 17 00:00:00 2001
From: jansupol <jan.supol@oracle.com>
Date: Tue, 6 Apr 2021 18:13:10 +0200
Subject: [PATCH 5/9] Lazy synchronized SSL Context initialization in the
 HttpUrlConnector

Signed-off-by: jansupol <jan.supol@oracle.com>
---
 .../glassfish/jersey/client/internal/HttpUrlConnector.java | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
index 72bceec3ad..21456b9c2e 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -73,7 +73,8 @@ public class HttpUrlConnector implements Connector {
     private static final String ALLOW_RESTRICTED_HEADERS_SYSTEM_PROPERTY = "sun.net.http.allowRestrictedHeaders";
     // Avoid multi-thread uses of HttpsURLConnection.getDefaultSSLSocketFactory() because it does not implement a
     // proper lazy-initialization. See https://github.com/jersey/jersey/issues/3293
-    private static final SSLSocketFactory DEFAULT_SSL_SOCKET_FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory();
+    private static final LazyValue<SSLSocketFactory> DEFAULT_SSL_SOCKET_FACTORY =
+            Values.lazy((Value<SSLSocketFactory>) () -> HttpsURLConnection.getDefaultSSLSocketFactory());
     // The list of restricted headers is extracted from sun.net.www.protocol.http.HttpURLConnection
     private static final String[] restrictedHeaders = {
             "Access-Control-Request-Headers",
@@ -305,7 +306,7 @@ protected void secureConnection(final JerseyClient client, final HttpURLConnecti
                 suc.setHostnameVerifier(verifier);
             }
 
-            if (DEFAULT_SSL_SOCKET_FACTORY == suc.getSSLSocketFactory()) {
+            if (DEFAULT_SSL_SOCKET_FACTORY.get() == suc.getSSLSocketFactory()) {
                 // indicates that the custom socket factory was not set
                 suc.setSSLSocketFactory(sslSocketFactory.get());
             }

From 66aeadd992e5649f511f2c6cbe7eddf8e923d0de Mon Sep 17 00:00:00 2001
From: jansupol <jan.supol@oracle.com>
Date: Tue, 6 Apr 2021 15:44:27 +0200
Subject: [PATCH 6/9] Replace null Configuration in ContainerRequest with an
 empty instance to prevent possible NPE

Signed-off-by: jansupol <jan.supol@oracle.com>
---
 .../glassfish/jersey/server/ContainerRequest.java  | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
index d67e8e7419..7823668fbf 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ContainerRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -31,6 +31,7 @@
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.ws.rs.RuntimeType;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.container.ContainerRequestFilter;
 import javax.ws.rs.container.ContainerResponseFilter;
@@ -63,6 +64,8 @@
 import org.glassfish.jersey.message.internal.OutboundJaxrsResponse;
 import org.glassfish.jersey.message.internal.TracingAwarePropertiesDelegate;
 import org.glassfish.jersey.message.internal.VariantSelector;
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.glassfish.jersey.model.internal.ComponentBag;
 import org.glassfish.jersey.model.internal.RankedProvider;
 import org.glassfish.jersey.process.Inflector;
 import org.glassfish.jersey.server.internal.LocalizationMessages;
@@ -119,7 +122,7 @@ public class ContainerRequest extends InboundMessageContext
     // True if the request is used in the response processing phase (for example in ContainerResponseFilter)
     private boolean inResponseProcessingPhase;
     // lazy PropertiesResolver
-    private LazyValue<PropertiesResolver> propertiesResolver = Values.lazy(
+    private final LazyValue<PropertiesResolver> propertiesResolver = Values.lazy(
             (Value<PropertiesResolver>) () -> PropertiesResolver.create(getConfiguration(), getPropertiesDelegate())
     );
 
@@ -188,7 +191,12 @@ public ContainerRequest(
             final String httpMethod,
             final SecurityContext securityContext,
             final PropertiesDelegate propertiesDelegate) {
-        this(baseUri, requestUri, httpMethod, securityContext, propertiesDelegate, (Configuration) null);
+        this(baseUri, requestUri, httpMethod, securityContext, propertiesDelegate,
+                new CommonConfig(RuntimeType.SERVER, ComponentBag.EXCLUDE_EMPTY) {
+                    {
+                        this.property(ContainerRequest.class.getName(), Deprecated.class.getSimpleName());
+                    }
+                });
     }
 
     /**

From 339e30a88127a76bb56a80d7008f353200b32050 Mon Sep 17 00:00:00 2001
From: jansupol <jan.supol@oracle.com>
Date: Tue, 30 Mar 2021 14:20:07 +0200
Subject: [PATCH 7/9] Adopted Jackson 2.12.2. No change in repackaged Jackson.

Signed-off-by: jansupol <jan.supol@oracle.com>
---
 pom.xml | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index bc916373b6..0087da9392 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,7 +61,7 @@
 
     <developers>
         <developer>
-            <name>Roman Grigoriadi</name>
+            <name>Jorge Bescos Gascon</name>
             <organization>Oracle Corporation</organization>
             <organizationUrl>http://www.oracle.com/</organizationUrl>
         </developer>
@@ -74,6 +74,12 @@
             <name>Dmitry Kornilov</name>
             <organization>Oracle Corporation</organization>
             <organizationUrl>http://www.oracle.com/</organizationUrl>
+            <url>https://dmitrykornilov.net</url>
+        </developer>
+        <developer>
+            <name>David Kral</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
         </developer>
         <developer>
             <name>Tomas Kraus</name>
@@ -2099,7 +2105,7 @@
         <hk2.jvnet.osgi.version>org.jvnet.hk2.*;version="[2.5,4)"</hk2.jvnet.osgi.version>
         <hk2.config.version>5.1.0</hk2.config.version>
         <httpclient.version>4.5.13</httpclient.version>
-        <jackson.version>2.11.3</jackson.version>
+        <jackson.version>2.12.2</jackson.version>
         <jackson1.version>1.9.13</jackson1.version>
         <javassist.version>3.25.0-GA</javassist.version>
         <jboss.logging.version>3.3.0.Final</jboss.logging.version>

From 9647ce541718cd0b583ceba1c735a886ec400b78 Mon Sep 17 00:00:00 2001
From: jansupol <jan.supol@oracle.com>
Date: Thu, 8 Apr 2021 16:21:33 +0200
Subject: [PATCH 8/9] Uptake Helidon 2.2.1

Signed-off-by: jansupol <jan.supol@oracle.com>
---
 connectors/helidon-connector/pom.xml              | 15 ++-------------
 .../helidon/connector/sse/EventOutputTest.java    |  4 +---
 2 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/connectors/helidon-connector/pom.xml b/connectors/helidon-connector/pom.xml
index 5d2b443f52..fc138f2a47 100644
--- a/connectors/helidon-connector/pom.xml
+++ b/connectors/helidon-connector/pom.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 
-    Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -34,8 +34,7 @@
         <dependency>
             <groupId>io.helidon.jersey</groupId>
             <artifactId>helidon-jersey-connector</artifactId>
-            <!-- Use 2.0.3 when available -->
-            <version>2.0.2</version>
+            <version>2.2.1</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -81,16 +80,6 @@
                    <detectJavaApiLink>false</detectJavaApiLink>
                </configuration>
             </plugin>
-            <plugin>
-                <!-- See https://github.com/oracle/helidon/issues/2371 -->
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-surefire-plugin</artifactId>
-                <configuration>
-                    <excludes>
-                        <exclude>**/*Test.java</exclude>
-                    </excludes>
-                </configuration>
-            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/sse/EventOutputTest.java b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/sse/EventOutputTest.java
index 31ceb61e8d..5eb76e8e50 100644
--- a/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/sse/EventOutputTest.java
+++ b/connectors/helidon-connector/src/test/java/org/glassfish/jersey/helidon/connector/sse/EventOutputTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -139,7 +139,6 @@ public EventOutput getCommentsOnlyStream() throws IOException {
     }
 
     @Test
-    @Ignore //2.0.0-M3
     public void testReadSseEventAsPlainString() throws Exception {
         final Response r = target().path("test/single").request().get(Response.class);
         assertThat(r.readEntity(String.class), containsString("single"));
@@ -189,7 +188,6 @@ public void onEvent(InboundEvent inboundEvent) {
     }
 
     @Test
-    @Ignore // 2.0.0-M3
     public void testReadFromClosedOutput() throws Exception {
         /**
          * Need to disable HTTP Keep-Alive to prevent this test from hanging in HttpURLConnection

From 5f9510978b1c8077f3ed54cf8cbce0776e64cbba Mon Sep 17 00:00:00 2001
From: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
Date: Tue, 2 Mar 2021 15:11:23 +0100
Subject: [PATCH 9/9] ParameterConverter throwing IllegalArgumentException MUST
 result in 404

Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
---
 .../internal/inject/SingleValueExtractor.java |   8 +-
 .../server/IllegalArgumentExceptionTest.java  | 124 ++++++++++++++++++
 2 files changed, 130 insertions(+), 2 deletions(-)
 create mode 100644 tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/IllegalArgumentExceptionTest.java

diff --git a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/SingleValueExtractor.java b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/SingleValueExtractor.java
index 69431fb157..8eb21d5660 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/SingleValueExtractor.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/internal/inject/SingleValueExtractor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -62,7 +62,11 @@ public T extract(final MultivaluedMap<String, String> parameters) {
         } catch (final WebApplicationException | ProcessingException ex) {
             throw ex;
         } catch (final IllegalArgumentException ex) {
-            return defaultValue();
+            if (value == null) {
+                return defaultValue();
+            } else {
+                throw new ExtractorException(ex);
+            }
         } catch (final Exception ex) {
             throw new ExtractorException(ex);
         }
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/IllegalArgumentExceptionTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/IllegalArgumentExceptionTest.java
new file mode 100644
index 0000000000..3e27023f0c
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/IllegalArgumentExceptionTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.server;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ParamConverter;
+import javax.ws.rs.ext.ParamConverterProvider;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class IllegalArgumentExceptionTest extends JerseyTest {
+
+    private static final String PARAM_NAME = "paramName";
+
+    @Override
+    protected Application configure() {
+        return new ResourceConfig(TestResource.class, TestParamProvider.class);
+    }
+
+    @Path("test")
+    public static class TestResource {
+        @GET
+        @Path("1")
+        public String get1(@QueryParam(PARAM_NAME) CustomObj value) {
+            return "ok";
+        }
+        @GET
+        @Path("2")
+        public String get2(@NotNull @QueryParam(PARAM_NAME) String value) {
+            return "ok";
+        }
+        @GET
+        @Path("3")
+        public String get3(@NotNull @DefaultValue("get3") @QueryParam(PARAM_NAME) CustomObj value) {
+            return value.value;
+        }
+        @GET
+        @Path("4")
+        public String get4(@HeaderParam(PARAM_NAME) CustomObj header) {
+            return "ok";
+        }
+    }
+
+    @Test
+    public void illegalArgumentExceptionWith404() {
+        Response response = target().path("test/1").queryParam(PARAM_NAME, 1).request().get();
+        assertEquals(404, response.getStatus());
+    }
+
+    @Test
+    public void validationExceptionWith400() {
+        Response response = target().path("test/2").request().get();
+        assertEquals(400, response.getStatus());
+    }
+
+    @Test
+    public void with200() {
+        Response response = target().path("test/3").request().get();
+        assertEquals(200, response.getStatus());
+        assertEquals("get3", response.readEntity(String.class));
+    }
+
+    @Test
+    public void validationExceptionHeaderWith400() {
+        Response response = target().path("test/4").request().header(PARAM_NAME, "1").get();
+        assertEquals(400, response.getStatus());
+    }
+
+    private static class CustomObj {
+        private String value;
+    }
+
+    public static class TestParamProvider implements ParamConverterProvider {
+        @Override
+        public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
+            return (ParamConverter<T>) new ParamConverter<CustomObj>() {
+                @Override
+                public CustomObj fromString(String value) {
+                    if ("1".equals(value)) {
+                        throw new IllegalArgumentException("test exception");
+                    } else if (value != null) {
+                        CustomObj obj = new CustomObj();
+                        obj.value = value;
+                        return obj;
+                    } else {
+                        return null;
+                    }
+                }
+                @Override
+                public String toString(CustomObj value) {
+                    return null;
+                }
+            };
+        }
+    }
+}